roojs-core-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      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * 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>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * 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>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @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'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @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'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @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'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @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'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @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'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @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'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 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).
4779 <p>
4780 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>
4781
4782 <p>
4783 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.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <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>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <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>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * 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;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @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).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * 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)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * 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).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * 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).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * 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).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * 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).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * 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).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * 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.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <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
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @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}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @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.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 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)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * 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.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls)
8864         {
8865             if(this.getStyle("position") == "static"){
8866                 this.setStyle("position", "relative");
8867             }
8868             if(!this._mask){
8869                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8870             }
8871             this.addClass("x-masked");
8872             this._mask.setDisplayed(true);
8873             
8874             // we wander
8875             var z = 0;
8876             var dom = this.dom
8877             while (dom && dom.style) {
8878                 if (!isNaN(parseInt(dom.style.zIndex))) {
8879                     z = Math.max(z, parseInt(dom.style.zIndex));
8880                 }
8881                 dom = dom.parentNode;
8882             }
8883             // if we are masking the body - then it hides everything..
8884             if (this.dom == document.body) {
8885                 z = 1000000;
8886             }
8887            
8888             if(typeof msg == 'string'){
8889                 if(!this._maskMsg){
8890                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8891                 }
8892                 var mm = this._maskMsg;
8893                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8894                 mm.dom.firstChild.innerHTML = msg;
8895                 mm.setDisplayed(true);
8896                 mm.center(this);
8897                 mm.setStyle('z-index', z + 102);
8898             }
8899             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8900                 this._mask.setHeight(this.getHeight());
8901             }
8902             this._mask.setStyle('z-index', z + 100);
8903             
8904             return this._mask;
8905         },
8906
8907         /**
8908          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8909          * it is cached for reuse.
8910          */
8911         unmask : function(removeEl){
8912             if(this._mask){
8913                 if(removeEl === true){
8914                     this._mask.remove();
8915                     delete this._mask;
8916                     if(this._maskMsg){
8917                         this._maskMsg.remove();
8918                         delete this._maskMsg;
8919                     }
8920                 }else{
8921                     this._mask.setDisplayed(false);
8922                     if(this._maskMsg){
8923                         this._maskMsg.setDisplayed(false);
8924                     }
8925                 }
8926             }
8927             this.removeClass("x-masked");
8928         },
8929
8930         /**
8931          * Returns true if this element is masked
8932          * @return {Boolean}
8933          */
8934         isMasked : function(){
8935             return this._mask && this._mask.isVisible();
8936         },
8937
8938         /**
8939          * Creates an iframe shim for this element to keep selects and other windowed objects from
8940          * showing through.
8941          * @return {Roo.Element} The new shim element
8942          */
8943         createShim : function(){
8944             var el = document.createElement('iframe');
8945             el.frameBorder = 'no';
8946             el.className = 'roo-shim';
8947             if(Roo.isIE && Roo.isSecure){
8948                 el.src = Roo.SSL_SECURE_URL;
8949             }
8950             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8951             shim.autoBoxAdjust = false;
8952             return shim;
8953         },
8954
8955         /**
8956          * Removes this element from the DOM and deletes it from the cache
8957          */
8958         remove : function(){
8959             if(this.dom.parentNode){
8960                 this.dom.parentNode.removeChild(this.dom);
8961             }
8962             delete El.cache[this.dom.id];
8963         },
8964
8965         /**
8966          * Sets up event handlers to add and remove a css class when the mouse is over this element
8967          * @param {String} className
8968          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8969          * mouseout events for children elements
8970          * @return {Roo.Element} this
8971          */
8972         addClassOnOver : function(className, preventFlicker){
8973             this.on("mouseover", function(){
8974                 Roo.fly(this, '_internal').addClass(className);
8975             }, this.dom);
8976             var removeFn = function(e){
8977                 if(preventFlicker !== true || !e.within(this, true)){
8978                     Roo.fly(this, '_internal').removeClass(className);
8979                 }
8980             };
8981             this.on("mouseout", removeFn, this.dom);
8982             return this;
8983         },
8984
8985         /**
8986          * Sets up event handlers to add and remove a css class when this element has the focus
8987          * @param {String} className
8988          * @return {Roo.Element} this
8989          */
8990         addClassOnFocus : function(className){
8991             this.on("focus", function(){
8992                 Roo.fly(this, '_internal').addClass(className);
8993             }, this.dom);
8994             this.on("blur", function(){
8995                 Roo.fly(this, '_internal').removeClass(className);
8996             }, this.dom);
8997             return this;
8998         },
8999         /**
9000          * 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)
9001          * @param {String} className
9002          * @return {Roo.Element} this
9003          */
9004         addClassOnClick : function(className){
9005             var dom = this.dom;
9006             this.on("mousedown", function(){
9007                 Roo.fly(dom, '_internal').addClass(className);
9008                 var d = Roo.get(document);
9009                 var fn = function(){
9010                     Roo.fly(dom, '_internal').removeClass(className);
9011                     d.removeListener("mouseup", fn);
9012                 };
9013                 d.on("mouseup", fn);
9014             });
9015             return this;
9016         },
9017
9018         /**
9019          * Stops the specified event from bubbling and optionally prevents the default action
9020          * @param {String} eventName
9021          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9022          * @return {Roo.Element} this
9023          */
9024         swallowEvent : function(eventName, preventDefault){
9025             var fn = function(e){
9026                 e.stopPropagation();
9027                 if(preventDefault){
9028                     e.preventDefault();
9029                 }
9030             };
9031             if(eventName instanceof Array){
9032                 for(var i = 0, len = eventName.length; i < len; i++){
9033                      this.on(eventName[i], fn);
9034                 }
9035                 return this;
9036             }
9037             this.on(eventName, fn);
9038             return this;
9039         },
9040
9041         /**
9042          * @private
9043          */
9044       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9045
9046         /**
9047          * Sizes this element to its parent element's dimensions performing
9048          * neccessary box adjustments.
9049          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9050          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9051          * @return {Roo.Element} this
9052          */
9053         fitToParent : function(monitorResize, targetParent) {
9054           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9055           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9056           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9057             return;
9058           }
9059           var p = Roo.get(targetParent || this.dom.parentNode);
9060           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9061           if (monitorResize === true) {
9062             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9063             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9064           }
9065           return this;
9066         },
9067
9068         /**
9069          * Gets the next sibling, skipping text nodes
9070          * @return {HTMLElement} The next sibling or null
9071          */
9072         getNextSibling : function(){
9073             var n = this.dom.nextSibling;
9074             while(n && n.nodeType != 1){
9075                 n = n.nextSibling;
9076             }
9077             return n;
9078         },
9079
9080         /**
9081          * Gets the previous sibling, skipping text nodes
9082          * @return {HTMLElement} The previous sibling or null
9083          */
9084         getPrevSibling : function(){
9085             var n = this.dom.previousSibling;
9086             while(n && n.nodeType != 1){
9087                 n = n.previousSibling;
9088             }
9089             return n;
9090         },
9091
9092
9093         /**
9094          * Appends the passed element(s) to this element
9095          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9096          * @return {Roo.Element} this
9097          */
9098         appendChild: function(el){
9099             el = Roo.get(el);
9100             el.appendTo(this);
9101             return this;
9102         },
9103
9104         /**
9105          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9106          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9107          * automatically generated with the specified attributes.
9108          * @param {HTMLElement} insertBefore (optional) a child element of this element
9109          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9110          * @return {Roo.Element} The new child element
9111          */
9112         createChild: function(config, insertBefore, returnDom){
9113             config = config || {tag:'div'};
9114             if(insertBefore){
9115                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9116             }
9117             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9118         },
9119
9120         /**
9121          * Appends this element to the passed element
9122          * @param {String/HTMLElement/Element} el The new parent element
9123          * @return {Roo.Element} this
9124          */
9125         appendTo: function(el){
9126             el = Roo.getDom(el);
9127             el.appendChild(this.dom);
9128             return this;
9129         },
9130
9131         /**
9132          * Inserts this element before the passed element in the DOM
9133          * @param {String/HTMLElement/Element} el The element to insert before
9134          * @return {Roo.Element} this
9135          */
9136         insertBefore: function(el){
9137             el = Roo.getDom(el);
9138             el.parentNode.insertBefore(this.dom, el);
9139             return this;
9140         },
9141
9142         /**
9143          * Inserts this element after the passed element in the DOM
9144          * @param {String/HTMLElement/Element} el The element to insert after
9145          * @return {Roo.Element} this
9146          */
9147         insertAfter: function(el){
9148             el = Roo.getDom(el);
9149             el.parentNode.insertBefore(this.dom, el.nextSibling);
9150             return this;
9151         },
9152
9153         /**
9154          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9155          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9156          * @return {Roo.Element} The new child
9157          */
9158         insertFirst: function(el, returnDom){
9159             el = el || {};
9160             if(typeof el == 'object' && !el.nodeType){ // dh config
9161                 return this.createChild(el, this.dom.firstChild, returnDom);
9162             }else{
9163                 el = Roo.getDom(el);
9164                 this.dom.insertBefore(el, this.dom.firstChild);
9165                 return !returnDom ? Roo.get(el) : el;
9166             }
9167         },
9168
9169         /**
9170          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9171          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9172          * @param {String} where (optional) 'before' or 'after' defaults to before
9173          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9174          * @return {Roo.Element} the inserted Element
9175          */
9176         insertSibling: function(el, where, returnDom){
9177             where = where ? where.toLowerCase() : 'before';
9178             el = el || {};
9179             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9180
9181             if(typeof el == 'object' && !el.nodeType){ // dh config
9182                 if(where == 'after' && !this.dom.nextSibling){
9183                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9184                 }else{
9185                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9186                 }
9187
9188             }else{
9189                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9190                             where == 'before' ? this.dom : this.dom.nextSibling);
9191                 if(!returnDom){
9192                     rt = Roo.get(rt);
9193                 }
9194             }
9195             return rt;
9196         },
9197
9198         /**
9199          * Creates and wraps this element with another element
9200          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9201          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9202          * @return {HTMLElement/Element} The newly created wrapper element
9203          */
9204         wrap: function(config, returnDom){
9205             if(!config){
9206                 config = {tag: "div"};
9207             }
9208             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9209             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9210             return newEl;
9211         },
9212
9213         /**
9214          * Replaces the passed element with this element
9215          * @param {String/HTMLElement/Element} el The element to replace
9216          * @return {Roo.Element} this
9217          */
9218         replace: function(el){
9219             el = Roo.get(el);
9220             this.insertBefore(el);
9221             el.remove();
9222             return this;
9223         },
9224
9225         /**
9226          * Inserts an html fragment into this element
9227          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9228          * @param {String} html The HTML fragment
9229          * @param {Boolean} returnEl True to return an Roo.Element
9230          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9231          */
9232         insertHtml : function(where, html, returnEl){
9233             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9234             return returnEl ? Roo.get(el) : el;
9235         },
9236
9237         /**
9238          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9239          * @param {Object} o The object with the attributes
9240          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9241          * @return {Roo.Element} this
9242          */
9243         set : function(o, useSet){
9244             var el = this.dom;
9245             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9246             for(var attr in o){
9247                 if(attr == "style" || typeof o[attr] == "function") continue;
9248                 if(attr=="cls"){
9249                     el.className = o["cls"];
9250                 }else{
9251                     if(useSet) el.setAttribute(attr, o[attr]);
9252                     else el[attr] = o[attr];
9253                 }
9254             }
9255             if(o.style){
9256                 Roo.DomHelper.applyStyles(el, o.style);
9257             }
9258             return this;
9259         },
9260
9261         /**
9262          * Convenience method for constructing a KeyMap
9263          * @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:
9264          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9265          * @param {Function} fn The function to call
9266          * @param {Object} scope (optional) The scope of the function
9267          * @return {Roo.KeyMap} The KeyMap created
9268          */
9269         addKeyListener : function(key, fn, scope){
9270             var config;
9271             if(typeof key != "object" || key instanceof Array){
9272                 config = {
9273                     key: key,
9274                     fn: fn,
9275                     scope: scope
9276                 };
9277             }else{
9278                 config = {
9279                     key : key.key,
9280                     shift : key.shift,
9281                     ctrl : key.ctrl,
9282                     alt : key.alt,
9283                     fn: fn,
9284                     scope: scope
9285                 };
9286             }
9287             return new Roo.KeyMap(this, config);
9288         },
9289
9290         /**
9291          * Creates a KeyMap for this element
9292          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9293          * @return {Roo.KeyMap} The KeyMap created
9294          */
9295         addKeyMap : function(config){
9296             return new Roo.KeyMap(this, config);
9297         },
9298
9299         /**
9300          * Returns true if this element is scrollable.
9301          * @return {Boolean}
9302          */
9303          isScrollable : function(){
9304             var dom = this.dom;
9305             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9306         },
9307
9308         /**
9309          * 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().
9310          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9311          * @param {Number} value The new scroll value
9312          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9313          * @return {Element} this
9314          */
9315
9316         scrollTo : function(side, value, animate){
9317             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9318             if(!animate || !A){
9319                 this.dom[prop] = value;
9320             }else{
9321                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9322                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9323             }
9324             return this;
9325         },
9326
9327         /**
9328          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9329          * within this element's scrollable range.
9330          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9331          * @param {Number} distance How far to scroll the element in pixels
9332          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9333          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9334          * was scrolled as far as it could go.
9335          */
9336          scroll : function(direction, distance, animate){
9337              if(!this.isScrollable()){
9338                  return;
9339              }
9340              var el = this.dom;
9341              var l = el.scrollLeft, t = el.scrollTop;
9342              var w = el.scrollWidth, h = el.scrollHeight;
9343              var cw = el.clientWidth, ch = el.clientHeight;
9344              direction = direction.toLowerCase();
9345              var scrolled = false;
9346              var a = this.preanim(arguments, 2);
9347              switch(direction){
9348                  case "l":
9349                  case "left":
9350                      if(w - l > cw){
9351                          var v = Math.min(l + distance, w-cw);
9352                          this.scrollTo("left", v, a);
9353                          scrolled = true;
9354                      }
9355                      break;
9356                 case "r":
9357                 case "right":
9358                      if(l > 0){
9359                          var v = Math.max(l - distance, 0);
9360                          this.scrollTo("left", v, a);
9361                          scrolled = true;
9362                      }
9363                      break;
9364                 case "t":
9365                 case "top":
9366                 case "up":
9367                      if(t > 0){
9368                          var v = Math.max(t - distance, 0);
9369                          this.scrollTo("top", v, a);
9370                          scrolled = true;
9371                      }
9372                      break;
9373                 case "b":
9374                 case "bottom":
9375                 case "down":
9376                      if(h - t > ch){
9377                          var v = Math.min(t + distance, h-ch);
9378                          this.scrollTo("top", v, a);
9379                          scrolled = true;
9380                      }
9381                      break;
9382              }
9383              return scrolled;
9384         },
9385
9386         /**
9387          * Translates the passed page coordinates into left/top css values for this element
9388          * @param {Number/Array} x The page x or an array containing [x, y]
9389          * @param {Number} y The page y
9390          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9391          */
9392         translatePoints : function(x, y){
9393             if(typeof x == 'object' || x instanceof Array){
9394                 y = x[1]; x = x[0];
9395             }
9396             var p = this.getStyle('position');
9397             var o = this.getXY();
9398
9399             var l = parseInt(this.getStyle('left'), 10);
9400             var t = parseInt(this.getStyle('top'), 10);
9401
9402             if(isNaN(l)){
9403                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9404             }
9405             if(isNaN(t)){
9406                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9407             }
9408
9409             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9410         },
9411
9412         /**
9413          * Returns the current scroll position of the element.
9414          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9415          */
9416         getScroll : function(){
9417             var d = this.dom, doc = document;
9418             if(d == doc || d == doc.body){
9419                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9420                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9421                 return {left: l, top: t};
9422             }else{
9423                 return {left: d.scrollLeft, top: d.scrollTop};
9424             }
9425         },
9426
9427         /**
9428          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9429          * are convert to standard 6 digit hex color.
9430          * @param {String} attr The css attribute
9431          * @param {String} defaultValue The default value to use when a valid color isn't found
9432          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9433          * YUI color anims.
9434          */
9435         getColor : function(attr, defaultValue, prefix){
9436             var v = this.getStyle(attr);
9437             if(!v || v == "transparent" || v == "inherit") {
9438                 return defaultValue;
9439             }
9440             var color = typeof prefix == "undefined" ? "#" : prefix;
9441             if(v.substr(0, 4) == "rgb("){
9442                 var rvs = v.slice(4, v.length -1).split(",");
9443                 for(var i = 0; i < 3; i++){
9444                     var h = parseInt(rvs[i]).toString(16);
9445                     if(h < 16){
9446                         h = "0" + h;
9447                     }
9448                     color += h;
9449                 }
9450             } else {
9451                 if(v.substr(0, 1) == "#"){
9452                     if(v.length == 4) {
9453                         for(var i = 1; i < 4; i++){
9454                             var c = v.charAt(i);
9455                             color +=  c + c;
9456                         }
9457                     }else if(v.length == 7){
9458                         color += v.substr(1);
9459                     }
9460                 }
9461             }
9462             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9463         },
9464
9465         /**
9466          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9467          * gradient background, rounded corners and a 4-way shadow.
9468          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9469          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9470          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9471          * @return {Roo.Element} this
9472          */
9473         boxWrap : function(cls){
9474             cls = cls || 'x-box';
9475             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9476             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9477             return el;
9478         },
9479
9480         /**
9481          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9482          * @param {String} namespace The namespace in which to look for the attribute
9483          * @param {String} name The attribute name
9484          * @return {String} The attribute value
9485          */
9486         getAttributeNS : Roo.isIE ? function(ns, name){
9487             var d = this.dom;
9488             var type = typeof d[ns+":"+name];
9489             if(type != 'undefined' && type != 'unknown'){
9490                 return d[ns+":"+name];
9491             }
9492             return d[name];
9493         } : function(ns, name){
9494             var d = this.dom;
9495             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9496         }
9497     };
9498
9499     var ep = El.prototype;
9500
9501     /**
9502      * Appends an event handler (Shorthand for addListener)
9503      * @param {String}   eventName     The type of event to append
9504      * @param {Function} fn        The method the event invokes
9505      * @param {Object} scope       (optional) The scope (this object) of the fn
9506      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9507      * @method
9508      */
9509     ep.on = ep.addListener;
9510         // backwards compat
9511     ep.mon = ep.addListener;
9512
9513     /**
9514      * Removes an event handler from this element (shorthand for removeListener)
9515      * @param {String} eventName the type of event to remove
9516      * @param {Function} fn the method the event invokes
9517      * @return {Roo.Element} this
9518      * @method
9519      */
9520     ep.un = ep.removeListener;
9521
9522     /**
9523      * true to automatically adjust width and height settings for box-model issues (default to true)
9524      */
9525     ep.autoBoxAdjust = true;
9526
9527     // private
9528     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9529
9530     // private
9531     El.addUnits = function(v, defaultUnit){
9532         if(v === "" || v == "auto"){
9533             return v;
9534         }
9535         if(v === undefined){
9536             return '';
9537         }
9538         if(typeof v == "number" || !El.unitPattern.test(v)){
9539             return v + (defaultUnit || 'px');
9540         }
9541         return v;
9542     };
9543
9544     // special markup used throughout Roo when box wrapping elements
9545     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>';
9546     /**
9547      * Visibility mode constant - Use visibility to hide element
9548      * @static
9549      * @type Number
9550      */
9551     El.VISIBILITY = 1;
9552     /**
9553      * Visibility mode constant - Use display to hide element
9554      * @static
9555      * @type Number
9556      */
9557     El.DISPLAY = 2;
9558
9559     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9560     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9561     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9562
9563
9564
9565     /**
9566      * @private
9567      */
9568     El.cache = {};
9569
9570     var docEl;
9571
9572     /**
9573      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9574      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9575      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9576      * @return {Element} The Element object
9577      * @static
9578      */
9579     El.get = function(el){
9580         var ex, elm, id;
9581         if(!el){ return null; }
9582         if(typeof el == "string"){ // element id
9583             if(!(elm = document.getElementById(el))){
9584                 return null;
9585             }
9586             if(ex = El.cache[el]){
9587                 ex.dom = elm;
9588             }else{
9589                 ex = El.cache[el] = new El(elm);
9590             }
9591             return ex;
9592         }else if(el.tagName){ // dom element
9593             if(!(id = el.id)){
9594                 id = Roo.id(el);
9595             }
9596             if(ex = El.cache[id]){
9597                 ex.dom = el;
9598             }else{
9599                 ex = El.cache[id] = new El(el);
9600             }
9601             return ex;
9602         }else if(el instanceof El){
9603             if(el != docEl){
9604                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9605                                                               // catch case where it hasn't been appended
9606                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9607             }
9608             return el;
9609         }else if(el.isComposite){
9610             return el;
9611         }else if(el instanceof Array){
9612             return El.select(el);
9613         }else if(el == document){
9614             // create a bogus element object representing the document object
9615             if(!docEl){
9616                 var f = function(){};
9617                 f.prototype = El.prototype;
9618                 docEl = new f();
9619                 docEl.dom = document;
9620             }
9621             return docEl;
9622         }
9623         return null;
9624     };
9625
9626     // private
9627     El.uncache = function(el){
9628         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9629             if(a[i]){
9630                 delete El.cache[a[i].id || a[i]];
9631             }
9632         }
9633     };
9634
9635     // private
9636     // Garbage collection - uncache elements/purge listeners on orphaned elements
9637     // so we don't hold a reference and cause the browser to retain them
9638     El.garbageCollect = function(){
9639         if(!Roo.enableGarbageCollector){
9640             clearInterval(El.collectorThread);
9641             return;
9642         }
9643         for(var eid in El.cache){
9644             var el = El.cache[eid], d = el.dom;
9645             // -------------------------------------------------------
9646             // Determining what is garbage:
9647             // -------------------------------------------------------
9648             // !d
9649             // dom node is null, definitely garbage
9650             // -------------------------------------------------------
9651             // !d.parentNode
9652             // no parentNode == direct orphan, definitely garbage
9653             // -------------------------------------------------------
9654             // !d.offsetParent && !document.getElementById(eid)
9655             // display none elements have no offsetParent so we will
9656             // also try to look it up by it's id. However, check
9657             // offsetParent first so we don't do unneeded lookups.
9658             // This enables collection of elements that are not orphans
9659             // directly, but somewhere up the line they have an orphan
9660             // parent.
9661             // -------------------------------------------------------
9662             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9663                 delete El.cache[eid];
9664                 if(d && Roo.enableListenerCollection){
9665                     E.purgeElement(d);
9666                 }
9667             }
9668         }
9669     }
9670     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9671
9672
9673     // dom is optional
9674     El.Flyweight = function(dom){
9675         this.dom = dom;
9676     };
9677     El.Flyweight.prototype = El.prototype;
9678
9679     El._flyweights = {};
9680     /**
9681      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9682      * the dom node can be overwritten by other code.
9683      * @param {String/HTMLElement} el The dom node or id
9684      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9685      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9686      * @static
9687      * @return {Element} The shared Element object
9688      */
9689     El.fly = function(el, named){
9690         named = named || '_global';
9691         el = Roo.getDom(el);
9692         if(!el){
9693             return null;
9694         }
9695         if(!El._flyweights[named]){
9696             El._flyweights[named] = new El.Flyweight();
9697         }
9698         El._flyweights[named].dom = el;
9699         return El._flyweights[named];
9700     };
9701
9702     /**
9703      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9704      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9705      * Shorthand of {@link Roo.Element#get}
9706      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9707      * @return {Element} The Element object
9708      * @member Roo
9709      * @method get
9710      */
9711     Roo.get = El.get;
9712     /**
9713      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9714      * the dom node can be overwritten by other code.
9715      * Shorthand of {@link Roo.Element#fly}
9716      * @param {String/HTMLElement} el The dom node or id
9717      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9718      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9719      * @static
9720      * @return {Element} The shared Element object
9721      * @member Roo
9722      * @method fly
9723      */
9724     Roo.fly = El.fly;
9725
9726     // speedy lookup for elements never to box adjust
9727     var noBoxAdjust = Roo.isStrict ? {
9728         select:1
9729     } : {
9730         input:1, select:1, textarea:1
9731     };
9732     if(Roo.isIE || Roo.isGecko){
9733         noBoxAdjust['button'] = 1;
9734     }
9735
9736
9737     Roo.EventManager.on(window, 'unload', function(){
9738         delete El.cache;
9739         delete El._flyweights;
9740     });
9741 })();
9742
9743
9744
9745
9746 if(Roo.DomQuery){
9747     Roo.Element.selectorFunction = Roo.DomQuery.select;
9748 }
9749
9750 Roo.Element.select = function(selector, unique, root){
9751     var els;
9752     if(typeof selector == "string"){
9753         els = Roo.Element.selectorFunction(selector, root);
9754     }else if(selector.length !== undefined){
9755         els = selector;
9756     }else{
9757         throw "Invalid selector";
9758     }
9759     if(unique === true){
9760         return new Roo.CompositeElement(els);
9761     }else{
9762         return new Roo.CompositeElementLite(els);
9763     }
9764 };
9765 /**
9766  * Selects elements based on the passed CSS selector to enable working on them as 1.
9767  * @param {String/Array} selector The CSS selector or an array of elements
9768  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9769  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9770  * @return {CompositeElementLite/CompositeElement}
9771  * @member Roo
9772  * @method select
9773  */
9774 Roo.select = Roo.Element.select;
9775
9776
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789 /*
9790  * Based on:
9791  * Ext JS Library 1.1.1
9792  * Copyright(c) 2006-2007, Ext JS, LLC.
9793  *
9794  * Originally Released Under LGPL - original licence link has changed is not relivant.
9795  *
9796  * Fork - LGPL
9797  * <script type="text/javascript">
9798  */
9799
9800
9801
9802 //Notifies Element that fx methods are available
9803 Roo.enableFx = true;
9804
9805 /**
9806  * @class Roo.Fx
9807  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9808  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9809  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9810  * Element effects to work.</p><br/>
9811  *
9812  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9813  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9814  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9815  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9816  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9817  * expected results and should be done with care.</p><br/>
9818  *
9819  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9820  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9821 <pre>
9822 Value  Description
9823 -----  -----------------------------
9824 tl     The top left corner
9825 t      The center of the top edge
9826 tr     The top right corner
9827 l      The center of the left edge
9828 r      The center of the right edge
9829 bl     The bottom left corner
9830 b      The center of the bottom edge
9831 br     The bottom right corner
9832 </pre>
9833  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9834  * below are common options that can be passed to any Fx method.</b>
9835  * @cfg {Function} callback A function called when the effect is finished
9836  * @cfg {Object} scope The scope of the effect function
9837  * @cfg {String} easing A valid Easing value for the effect
9838  * @cfg {String} afterCls A css class to apply after the effect
9839  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9840  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9841  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9842  * effects that end with the element being visually hidden, ignored otherwise)
9843  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9844  * a function which returns such a specification that will be applied to the Element after the effect finishes
9845  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9846  * @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
9847  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9848  */
9849 Roo.Fx = {
9850         /**
9851          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9852          * origin for the slide effect.  This function automatically handles wrapping the element with
9853          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9854          * Usage:
9855          *<pre><code>
9856 // default: slide the element in from the top
9857 el.slideIn();
9858
9859 // custom: slide the element in from the right with a 2-second duration
9860 el.slideIn('r', { duration: 2 });
9861
9862 // common config options shown with default values
9863 el.slideIn('t', {
9864     easing: 'easeOut',
9865     duration: .5
9866 });
9867 </code></pre>
9868          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9869          * @param {Object} options (optional) Object literal with any of the Fx config options
9870          * @return {Roo.Element} The Element
9871          */
9872     slideIn : function(anchor, o){
9873         var el = this.getFxEl();
9874         o = o || {};
9875
9876         el.queueFx(o, function(){
9877
9878             anchor = anchor || "t";
9879
9880             // fix display to visibility
9881             this.fixDisplay();
9882
9883             // restore values after effect
9884             var r = this.getFxRestore();
9885             var b = this.getBox();
9886             // fixed size for slide
9887             this.setSize(b);
9888
9889             // wrap if needed
9890             var wrap = this.fxWrap(r.pos, o, "hidden");
9891
9892             var st = this.dom.style;
9893             st.visibility = "visible";
9894             st.position = "absolute";
9895
9896             // clear out temp styles after slide and unwrap
9897             var after = function(){
9898                 el.fxUnwrap(wrap, r.pos, o);
9899                 st.width = r.width;
9900                 st.height = r.height;
9901                 el.afterFx(o);
9902             };
9903             // time to calc the positions
9904             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9905
9906             switch(anchor.toLowerCase()){
9907                 case "t":
9908                     wrap.setSize(b.width, 0);
9909                     st.left = st.bottom = "0";
9910                     a = {height: bh};
9911                 break;
9912                 case "l":
9913                     wrap.setSize(0, b.height);
9914                     st.right = st.top = "0";
9915                     a = {width: bw};
9916                 break;
9917                 case "r":
9918                     wrap.setSize(0, b.height);
9919                     wrap.setX(b.right);
9920                     st.left = st.top = "0";
9921                     a = {width: bw, points: pt};
9922                 break;
9923                 case "b":
9924                     wrap.setSize(b.width, 0);
9925                     wrap.setY(b.bottom);
9926                     st.left = st.top = "0";
9927                     a = {height: bh, points: pt};
9928                 break;
9929                 case "tl":
9930                     wrap.setSize(0, 0);
9931                     st.right = st.bottom = "0";
9932                     a = {width: bw, height: bh};
9933                 break;
9934                 case "bl":
9935                     wrap.setSize(0, 0);
9936                     wrap.setY(b.y+b.height);
9937                     st.right = st.top = "0";
9938                     a = {width: bw, height: bh, points: pt};
9939                 break;
9940                 case "br":
9941                     wrap.setSize(0, 0);
9942                     wrap.setXY([b.right, b.bottom]);
9943                     st.left = st.top = "0";
9944                     a = {width: bw, height: bh, points: pt};
9945                 break;
9946                 case "tr":
9947                     wrap.setSize(0, 0);
9948                     wrap.setX(b.x+b.width);
9949                     st.left = st.bottom = "0";
9950                     a = {width: bw, height: bh, points: pt};
9951                 break;
9952             }
9953             this.dom.style.visibility = "visible";
9954             wrap.show();
9955
9956             arguments.callee.anim = wrap.fxanim(a,
9957                 o,
9958                 'motion',
9959                 .5,
9960                 'easeOut', after);
9961         });
9962         return this;
9963     },
9964     
9965         /**
9966          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9967          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9968          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9969          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9970          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9971          * Usage:
9972          *<pre><code>
9973 // default: slide the element out to the top
9974 el.slideOut();
9975
9976 // custom: slide the element out to the right with a 2-second duration
9977 el.slideOut('r', { duration: 2 });
9978
9979 // common config options shown with default values
9980 el.slideOut('t', {
9981     easing: 'easeOut',
9982     duration: .5,
9983     remove: false,
9984     useDisplay: false
9985 });
9986 </code></pre>
9987          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9988          * @param {Object} options (optional) Object literal with any of the Fx config options
9989          * @return {Roo.Element} The Element
9990          */
9991     slideOut : function(anchor, o){
9992         var el = this.getFxEl();
9993         o = o || {};
9994
9995         el.queueFx(o, function(){
9996
9997             anchor = anchor || "t";
9998
9999             // restore values after effect
10000             var r = this.getFxRestore();
10001             
10002             var b = this.getBox();
10003             // fixed size for slide
10004             this.setSize(b);
10005
10006             // wrap if needed
10007             var wrap = this.fxWrap(r.pos, o, "visible");
10008
10009             var st = this.dom.style;
10010             st.visibility = "visible";
10011             st.position = "absolute";
10012
10013             wrap.setSize(b);
10014
10015             var after = function(){
10016                 if(o.useDisplay){
10017                     el.setDisplayed(false);
10018                 }else{
10019                     el.hide();
10020                 }
10021
10022                 el.fxUnwrap(wrap, r.pos, o);
10023
10024                 st.width = r.width;
10025                 st.height = r.height;
10026
10027                 el.afterFx(o);
10028             };
10029
10030             var a, zero = {to: 0};
10031             switch(anchor.toLowerCase()){
10032                 case "t":
10033                     st.left = st.bottom = "0";
10034                     a = {height: zero};
10035                 break;
10036                 case "l":
10037                     st.right = st.top = "0";
10038                     a = {width: zero};
10039                 break;
10040                 case "r":
10041                     st.left = st.top = "0";
10042                     a = {width: zero, points: {to:[b.right, b.y]}};
10043                 break;
10044                 case "b":
10045                     st.left = st.top = "0";
10046                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10047                 break;
10048                 case "tl":
10049                     st.right = st.bottom = "0";
10050                     a = {width: zero, height: zero};
10051                 break;
10052                 case "bl":
10053                     st.right = st.top = "0";
10054                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10055                 break;
10056                 case "br":
10057                     st.left = st.top = "0";
10058                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10059                 break;
10060                 case "tr":
10061                     st.left = st.bottom = "0";
10062                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10063                 break;
10064             }
10065
10066             arguments.callee.anim = wrap.fxanim(a,
10067                 o,
10068                 'motion',
10069                 .5,
10070                 "easeOut", after);
10071         });
10072         return this;
10073     },
10074
10075         /**
10076          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10077          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10078          * The element must be removed from the DOM using the 'remove' config option if desired.
10079          * Usage:
10080          *<pre><code>
10081 // default
10082 el.puff();
10083
10084 // common config options shown with default values
10085 el.puff({
10086     easing: 'easeOut',
10087     duration: .5,
10088     remove: false,
10089     useDisplay: false
10090 });
10091 </code></pre>
10092          * @param {Object} options (optional) Object literal with any of the Fx config options
10093          * @return {Roo.Element} The Element
10094          */
10095     puff : function(o){
10096         var el = this.getFxEl();
10097         o = o || {};
10098
10099         el.queueFx(o, function(){
10100             this.clearOpacity();
10101             this.show();
10102
10103             // restore values after effect
10104             var r = this.getFxRestore();
10105             var st = this.dom.style;
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.clearOpacity();
10115
10116                 el.setPositioning(r.pos);
10117                 st.width = r.width;
10118                 st.height = r.height;
10119                 st.fontSize = '';
10120                 el.afterFx(o);
10121             };
10122
10123             var width = this.getWidth();
10124             var height = this.getHeight();
10125
10126             arguments.callee.anim = this.fxanim({
10127                     width : {to: this.adjustWidth(width * 2)},
10128                     height : {to: this.adjustHeight(height * 2)},
10129                     points : {by: [-(width * .5), -(height * .5)]},
10130                     opacity : {to: 0},
10131                     fontSize: {to:200, unit: "%"}
10132                 },
10133                 o,
10134                 'motion',
10135                 .5,
10136                 "easeOut", after);
10137         });
10138         return this;
10139     },
10140
10141         /**
10142          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10143          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10144          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10145          * Usage:
10146          *<pre><code>
10147 // default
10148 el.switchOff();
10149
10150 // all config options shown with default values
10151 el.switchOff({
10152     easing: 'easeIn',
10153     duration: .3,
10154     remove: false,
10155     useDisplay: false
10156 });
10157 </code></pre>
10158          * @param {Object} options (optional) Object literal with any of the Fx config options
10159          * @return {Roo.Element} The Element
10160          */
10161     switchOff : function(o){
10162         var el = this.getFxEl();
10163         o = o || {};
10164
10165         el.queueFx(o, function(){
10166             this.clearOpacity();
10167             this.clip();
10168
10169             // restore values after effect
10170             var r = this.getFxRestore();
10171             var st = this.dom.style;
10172
10173             var after = function(){
10174                 if(o.useDisplay){
10175                     el.setDisplayed(false);
10176                 }else{
10177                     el.hide();
10178                 }
10179
10180                 el.clearOpacity();
10181                 el.setPositioning(r.pos);
10182                 st.width = r.width;
10183                 st.height = r.height;
10184
10185                 el.afterFx(o);
10186             };
10187
10188             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10189                 this.clearOpacity();
10190                 (function(){
10191                     this.fxanim({
10192                         height:{to:1},
10193                         points:{by:[0, this.getHeight() * .5]}
10194                     }, o, 'motion', 0.3, 'easeIn', after);
10195                 }).defer(100, this);
10196             });
10197         });
10198         return this;
10199     },
10200
10201     /**
10202      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10203      * changed using the "attr" config option) and then fading back to the original color. If no original
10204      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10205      * Usage:
10206 <pre><code>
10207 // default: highlight background to yellow
10208 el.highlight();
10209
10210 // custom: highlight foreground text to blue for 2 seconds
10211 el.highlight("0000ff", { attr: 'color', duration: 2 });
10212
10213 // common config options shown with default values
10214 el.highlight("ffff9c", {
10215     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10216     endColor: (current color) or "ffffff",
10217     easing: 'easeIn',
10218     duration: 1
10219 });
10220 </code></pre>
10221      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10222      * @param {Object} options (optional) Object literal with any of the Fx config options
10223      * @return {Roo.Element} The Element
10224      */ 
10225     highlight : function(color, o){
10226         var el = this.getFxEl();
10227         o = o || {};
10228
10229         el.queueFx(o, function(){
10230             color = color || "ffff9c";
10231             attr = o.attr || "backgroundColor";
10232
10233             this.clearOpacity();
10234             this.show();
10235
10236             var origColor = this.getColor(attr);
10237             var restoreColor = this.dom.style[attr];
10238             endColor = (o.endColor || origColor) || "ffffff";
10239
10240             var after = function(){
10241                 el.dom.style[attr] = restoreColor;
10242                 el.afterFx(o);
10243             };
10244
10245             var a = {};
10246             a[attr] = {from: color, to: endColor};
10247             arguments.callee.anim = this.fxanim(a,
10248                 o,
10249                 'color',
10250                 1,
10251                 'easeIn', after);
10252         });
10253         return this;
10254     },
10255
10256    /**
10257     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10258     * Usage:
10259 <pre><code>
10260 // default: a single light blue ripple
10261 el.frame();
10262
10263 // custom: 3 red ripples lasting 3 seconds total
10264 el.frame("ff0000", 3, { duration: 3 });
10265
10266 // common config options shown with default values
10267 el.frame("C3DAF9", 1, {
10268     duration: 1 //duration of entire animation (not each individual ripple)
10269     // Note: Easing is not configurable and will be ignored if included
10270 });
10271 </code></pre>
10272     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10273     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10274     * @param {Object} options (optional) Object literal with any of the Fx config options
10275     * @return {Roo.Element} The Element
10276     */
10277     frame : function(color, count, o){
10278         var el = this.getFxEl();
10279         o = o || {};
10280
10281         el.queueFx(o, function(){
10282             color = color || "#C3DAF9";
10283             if(color.length == 6){
10284                 color = "#" + color;
10285             }
10286             count = count || 1;
10287             duration = o.duration || 1;
10288             this.show();
10289
10290             var b = this.getBox();
10291             var animFn = function(){
10292                 var proxy = this.createProxy({
10293
10294                      style:{
10295                         visbility:"hidden",
10296                         position:"absolute",
10297                         "z-index":"35000", // yee haw
10298                         border:"0px solid " + color
10299                      }
10300                   });
10301                 var scale = Roo.isBorderBox ? 2 : 1;
10302                 proxy.animate({
10303                     top:{from:b.y, to:b.y - 20},
10304                     left:{from:b.x, to:b.x - 20},
10305                     borderWidth:{from:0, to:10},
10306                     opacity:{from:1, to:0},
10307                     height:{from:b.height, to:(b.height + (20*scale))},
10308                     width:{from:b.width, to:(b.width + (20*scale))}
10309                 }, duration, function(){
10310                     proxy.remove();
10311                 });
10312                 if(--count > 0){
10313                      animFn.defer((duration/2)*1000, this);
10314                 }else{
10315                     el.afterFx(o);
10316                 }
10317             };
10318             animFn.call(this);
10319         });
10320         return this;
10321     },
10322
10323    /**
10324     * Creates a pause before any subsequent queued effects begin.  If there are
10325     * no effects queued after the pause it will have no effect.
10326     * Usage:
10327 <pre><code>
10328 el.pause(1);
10329 </code></pre>
10330     * @param {Number} seconds The length of time to pause (in seconds)
10331     * @return {Roo.Element} The Element
10332     */
10333     pause : function(seconds){
10334         var el = this.getFxEl();
10335         var o = {};
10336
10337         el.queueFx(o, function(){
10338             setTimeout(function(){
10339                 el.afterFx(o);
10340             }, seconds * 1000);
10341         });
10342         return this;
10343     },
10344
10345    /**
10346     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10347     * using the "endOpacity" config option.
10348     * Usage:
10349 <pre><code>
10350 // default: fade in from opacity 0 to 100%
10351 el.fadeIn();
10352
10353 // custom: fade in from opacity 0 to 75% over 2 seconds
10354 el.fadeIn({ endOpacity: .75, duration: 2});
10355
10356 // common config options shown with default values
10357 el.fadeIn({
10358     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10359     easing: 'easeOut',
10360     duration: .5
10361 });
10362 </code></pre>
10363     * @param {Object} options (optional) Object literal with any of the Fx config options
10364     * @return {Roo.Element} The Element
10365     */
10366     fadeIn : function(o){
10367         var el = this.getFxEl();
10368         o = o || {};
10369         el.queueFx(o, function(){
10370             this.setOpacity(0);
10371             this.fixDisplay();
10372             this.dom.style.visibility = 'visible';
10373             var to = o.endOpacity || 1;
10374             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10375                 o, null, .5, "easeOut", function(){
10376                 if(to == 1){
10377                     this.clearOpacity();
10378                 }
10379                 el.afterFx(o);
10380             });
10381         });
10382         return this;
10383     },
10384
10385    /**
10386     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10387     * using the "endOpacity" config option.
10388     * Usage:
10389 <pre><code>
10390 // default: fade out from the element's current opacity to 0
10391 el.fadeOut();
10392
10393 // custom: fade out from the element's current opacity to 25% over 2 seconds
10394 el.fadeOut({ endOpacity: .25, duration: 2});
10395
10396 // common config options shown with default values
10397 el.fadeOut({
10398     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10399     easing: 'easeOut',
10400     duration: .5
10401     remove: false,
10402     useDisplay: false
10403 });
10404 </code></pre>
10405     * @param {Object} options (optional) Object literal with any of the Fx config options
10406     * @return {Roo.Element} The Element
10407     */
10408     fadeOut : function(o){
10409         var el = this.getFxEl();
10410         o = o || {};
10411         el.queueFx(o, function(){
10412             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10413                 o, null, .5, "easeOut", function(){
10414                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10415                      this.dom.style.display = "none";
10416                 }else{
10417                      this.dom.style.visibility = "hidden";
10418                 }
10419                 this.clearOpacity();
10420                 el.afterFx(o);
10421             });
10422         });
10423         return this;
10424     },
10425
10426    /**
10427     * Animates the transition of an element's dimensions from a starting height/width
10428     * to an ending height/width.
10429     * Usage:
10430 <pre><code>
10431 // change height and width to 100x100 pixels
10432 el.scale(100, 100);
10433
10434 // common config options shown with default values.  The height and width will default to
10435 // the element's existing values if passed as null.
10436 el.scale(
10437     [element's width],
10438     [element's height], {
10439     easing: 'easeOut',
10440     duration: .35
10441 });
10442 </code></pre>
10443     * @param {Number} width  The new width (pass undefined to keep the original width)
10444     * @param {Number} height  The new height (pass undefined to keep the original height)
10445     * @param {Object} options (optional) Object literal with any of the Fx config options
10446     * @return {Roo.Element} The Element
10447     */
10448     scale : function(w, h, o){
10449         this.shift(Roo.apply({}, o, {
10450             width: w,
10451             height: h
10452         }));
10453         return this;
10454     },
10455
10456    /**
10457     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10458     * Any of these properties not specified in the config object will not be changed.  This effect 
10459     * requires that at least one new dimension, position or opacity setting must be passed in on
10460     * the config object in order for the function to have any effect.
10461     * Usage:
10462 <pre><code>
10463 // slide the element horizontally to x position 200 while changing the height and opacity
10464 el.shift({ x: 200, height: 50, opacity: .8 });
10465
10466 // common config options shown with default values.
10467 el.shift({
10468     width: [element's width],
10469     height: [element's height],
10470     x: [element's x position],
10471     y: [element's y position],
10472     opacity: [element's opacity],
10473     easing: 'easeOut',
10474     duration: .35
10475 });
10476 </code></pre>
10477     * @param {Object} options  Object literal with any of the Fx config options
10478     * @return {Roo.Element} The Element
10479     */
10480     shift : function(o){
10481         var el = this.getFxEl();
10482         o = o || {};
10483         el.queueFx(o, function(){
10484             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10485             if(w !== undefined){
10486                 a.width = {to: this.adjustWidth(w)};
10487             }
10488             if(h !== undefined){
10489                 a.height = {to: this.adjustHeight(h)};
10490             }
10491             if(x !== undefined || y !== undefined){
10492                 a.points = {to: [
10493                     x !== undefined ? x : this.getX(),
10494                     y !== undefined ? y : this.getY()
10495                 ]};
10496             }
10497             if(op !== undefined){
10498                 a.opacity = {to: op};
10499             }
10500             if(o.xy !== undefined){
10501                 a.points = {to: o.xy};
10502             }
10503             arguments.callee.anim = this.fxanim(a,
10504                 o, 'motion', .35, "easeOut", function(){
10505                 el.afterFx(o);
10506             });
10507         });
10508         return this;
10509     },
10510
10511         /**
10512          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10513          * ending point of the effect.
10514          * Usage:
10515          *<pre><code>
10516 // default: slide the element downward while fading out
10517 el.ghost();
10518
10519 // custom: slide the element out to the right with a 2-second duration
10520 el.ghost('r', { duration: 2 });
10521
10522 // common config options shown with default values
10523 el.ghost('b', {
10524     easing: 'easeOut',
10525     duration: .5
10526     remove: false,
10527     useDisplay: false
10528 });
10529 </code></pre>
10530          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10531          * @param {Object} options (optional) Object literal with any of the Fx config options
10532          * @return {Roo.Element} The Element
10533          */
10534     ghost : function(anchor, o){
10535         var el = this.getFxEl();
10536         o = o || {};
10537
10538         el.queueFx(o, function(){
10539             anchor = anchor || "b";
10540
10541             // restore values after effect
10542             var r = this.getFxRestore();
10543             var w = this.getWidth(),
10544                 h = this.getHeight();
10545
10546             var st = this.dom.style;
10547
10548             var after = function(){
10549                 if(o.useDisplay){
10550                     el.setDisplayed(false);
10551                 }else{
10552                     el.hide();
10553                 }
10554
10555                 el.clearOpacity();
10556                 el.setPositioning(r.pos);
10557                 st.width = r.width;
10558                 st.height = r.height;
10559
10560                 el.afterFx(o);
10561             };
10562
10563             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10564             switch(anchor.toLowerCase()){
10565                 case "t":
10566                     pt.by = [0, -h];
10567                 break;
10568                 case "l":
10569                     pt.by = [-w, 0];
10570                 break;
10571                 case "r":
10572                     pt.by = [w, 0];
10573                 break;
10574                 case "b":
10575                     pt.by = [0, h];
10576                 break;
10577                 case "tl":
10578                     pt.by = [-w, -h];
10579                 break;
10580                 case "bl":
10581                     pt.by = [-w, h];
10582                 break;
10583                 case "br":
10584                     pt.by = [w, h];
10585                 break;
10586                 case "tr":
10587                     pt.by = [w, -h];
10588                 break;
10589             }
10590
10591             arguments.callee.anim = this.fxanim(a,
10592                 o,
10593                 'motion',
10594                 .5,
10595                 "easeOut", after);
10596         });
10597         return this;
10598     },
10599
10600         /**
10601          * Ensures that all effects queued after syncFx is called on the element are
10602          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10603          * @return {Roo.Element} The Element
10604          */
10605     syncFx : function(){
10606         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10607             block : false,
10608             concurrent : true,
10609             stopFx : false
10610         });
10611         return this;
10612     },
10613
10614         /**
10615          * Ensures that all effects queued after sequenceFx is called on the element are
10616          * run in sequence.  This is the opposite of {@link #syncFx}.
10617          * @return {Roo.Element} The Element
10618          */
10619     sequenceFx : function(){
10620         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10621             block : false,
10622             concurrent : false,
10623             stopFx : false
10624         });
10625         return this;
10626     },
10627
10628         /* @private */
10629     nextFx : function(){
10630         var ef = this.fxQueue[0];
10631         if(ef){
10632             ef.call(this);
10633         }
10634     },
10635
10636         /**
10637          * Returns true if the element has any effects actively running or queued, else returns false.
10638          * @return {Boolean} True if element has active effects, else false
10639          */
10640     hasActiveFx : function(){
10641         return this.fxQueue && this.fxQueue[0];
10642     },
10643
10644         /**
10645          * Stops any running effects and clears the element's internal effects queue if it contains
10646          * any additional effects that haven't started yet.
10647          * @return {Roo.Element} The Element
10648          */
10649     stopFx : function(){
10650         if(this.hasActiveFx()){
10651             var cur = this.fxQueue[0];
10652             if(cur && cur.anim && cur.anim.isAnimated()){
10653                 this.fxQueue = [cur]; // clear out others
10654                 cur.anim.stop(true);
10655             }
10656         }
10657         return this;
10658     },
10659
10660         /* @private */
10661     beforeFx : function(o){
10662         if(this.hasActiveFx() && !o.concurrent){
10663            if(o.stopFx){
10664                this.stopFx();
10665                return true;
10666            }
10667            return false;
10668         }
10669         return true;
10670     },
10671
10672         /**
10673          * Returns true if the element is currently blocking so that no other effect can be queued
10674          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10675          * used to ensure that an effect initiated by a user action runs to completion prior to the
10676          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10677          * @return {Boolean} True if blocking, else false
10678          */
10679     hasFxBlock : function(){
10680         var q = this.fxQueue;
10681         return q && q[0] && q[0].block;
10682     },
10683
10684         /* @private */
10685     queueFx : function(o, fn){
10686         if(!this.fxQueue){
10687             this.fxQueue = [];
10688         }
10689         if(!this.hasFxBlock()){
10690             Roo.applyIf(o, this.fxDefaults);
10691             if(!o.concurrent){
10692                 var run = this.beforeFx(o);
10693                 fn.block = o.block;
10694                 this.fxQueue.push(fn);
10695                 if(run){
10696                     this.nextFx();
10697                 }
10698             }else{
10699                 fn.call(this);
10700             }
10701         }
10702         return this;
10703     },
10704
10705         /* @private */
10706     fxWrap : function(pos, o, vis){
10707         var wrap;
10708         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10709             var wrapXY;
10710             if(o.fixPosition){
10711                 wrapXY = this.getXY();
10712             }
10713             var div = document.createElement("div");
10714             div.style.visibility = vis;
10715             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10716             wrap.setPositioning(pos);
10717             if(wrap.getStyle("position") == "static"){
10718                 wrap.position("relative");
10719             }
10720             this.clearPositioning('auto');
10721             wrap.clip();
10722             wrap.dom.appendChild(this.dom);
10723             if(wrapXY){
10724                 wrap.setXY(wrapXY);
10725             }
10726         }
10727         return wrap;
10728     },
10729
10730         /* @private */
10731     fxUnwrap : function(wrap, pos, o){
10732         this.clearPositioning();
10733         this.setPositioning(pos);
10734         if(!o.wrap){
10735             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10736             wrap.remove();
10737         }
10738     },
10739
10740         /* @private */
10741     getFxRestore : function(){
10742         var st = this.dom.style;
10743         return {pos: this.getPositioning(), width: st.width, height : st.height};
10744     },
10745
10746         /* @private */
10747     afterFx : function(o){
10748         if(o.afterStyle){
10749             this.applyStyles(o.afterStyle);
10750         }
10751         if(o.afterCls){
10752             this.addClass(o.afterCls);
10753         }
10754         if(o.remove === true){
10755             this.remove();
10756         }
10757         Roo.callback(o.callback, o.scope, [this]);
10758         if(!o.concurrent){
10759             this.fxQueue.shift();
10760             this.nextFx();
10761         }
10762     },
10763
10764         /* @private */
10765     getFxEl : function(){ // support for composite element fx
10766         return Roo.get(this.dom);
10767     },
10768
10769         /* @private */
10770     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10771         animType = animType || 'run';
10772         opt = opt || {};
10773         var anim = Roo.lib.Anim[animType](
10774             this.dom, args,
10775             (opt.duration || defaultDur) || .35,
10776             (opt.easing || defaultEase) || 'easeOut',
10777             function(){
10778                 Roo.callback(cb, this);
10779             },
10780             this
10781         );
10782         opt.anim = anim;
10783         return anim;
10784     }
10785 };
10786
10787 // backwords compat
10788 Roo.Fx.resize = Roo.Fx.scale;
10789
10790 //When included, Roo.Fx is automatically applied to Element so that all basic
10791 //effects are available directly via the Element API
10792 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10793  * Based on:
10794  * Ext JS Library 1.1.1
10795  * Copyright(c) 2006-2007, Ext JS, LLC.
10796  *
10797  * Originally Released Under LGPL - original licence link has changed is not relivant.
10798  *
10799  * Fork - LGPL
10800  * <script type="text/javascript">
10801  */
10802
10803
10804 /**
10805  * @class Roo.CompositeElement
10806  * Standard composite class. Creates a Roo.Element for every element in the collection.
10807  * <br><br>
10808  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10809  * actions will be performed on all the elements in this collection.</b>
10810  * <br><br>
10811  * All methods return <i>this</i> and can be chained.
10812  <pre><code>
10813  var els = Roo.select("#some-el div.some-class", true);
10814  // or select directly from an existing element
10815  var el = Roo.get('some-el');
10816  el.select('div.some-class', true);
10817
10818  els.setWidth(100); // all elements become 100 width
10819  els.hide(true); // all elements fade out and hide
10820  // or
10821  els.setWidth(100).hide(true);
10822  </code></pre>
10823  */
10824 Roo.CompositeElement = function(els){
10825     this.elements = [];
10826     this.addElements(els);
10827 };
10828 Roo.CompositeElement.prototype = {
10829     isComposite: true,
10830     addElements : function(els){
10831         if(!els) return this;
10832         if(typeof els == "string"){
10833             els = Roo.Element.selectorFunction(els);
10834         }
10835         var yels = this.elements;
10836         var index = yels.length-1;
10837         for(var i = 0, len = els.length; i < len; i++) {
10838                 yels[++index] = Roo.get(els[i]);
10839         }
10840         return this;
10841     },
10842
10843     /**
10844     * Clears this composite and adds the elements returned by the passed selector.
10845     * @param {String/Array} els A string CSS selector, an array of elements or an element
10846     * @return {CompositeElement} this
10847     */
10848     fill : function(els){
10849         this.elements = [];
10850         this.add(els);
10851         return this;
10852     },
10853
10854     /**
10855     * Filters this composite to only elements that match the passed selector.
10856     * @param {String} selector A string CSS selector
10857     * @return {CompositeElement} this
10858     */
10859     filter : function(selector){
10860         var els = [];
10861         this.each(function(el){
10862             if(el.is(selector)){
10863                 els[els.length] = el.dom;
10864             }
10865         });
10866         this.fill(els);
10867         return this;
10868     },
10869
10870     invoke : function(fn, args){
10871         var els = this.elements;
10872         for(var i = 0, len = els.length; i < len; i++) {
10873                 Roo.Element.prototype[fn].apply(els[i], args);
10874         }
10875         return this;
10876     },
10877     /**
10878     * Adds elements to this composite.
10879     * @param {String/Array} els A string CSS selector, an array of elements or an element
10880     * @return {CompositeElement} this
10881     */
10882     add : function(els){
10883         if(typeof els == "string"){
10884             this.addElements(Roo.Element.selectorFunction(els));
10885         }else if(els.length !== undefined){
10886             this.addElements(els);
10887         }else{
10888             this.addElements([els]);
10889         }
10890         return this;
10891     },
10892     /**
10893     * Calls the passed function passing (el, this, index) for each element in this composite.
10894     * @param {Function} fn The function to call
10895     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10896     * @return {CompositeElement} this
10897     */
10898     each : function(fn, scope){
10899         var els = this.elements;
10900         for(var i = 0, len = els.length; i < len; i++){
10901             if(fn.call(scope || els[i], els[i], this, i) === false) {
10902                 break;
10903             }
10904         }
10905         return this;
10906     },
10907
10908     /**
10909      * Returns the Element object at the specified index
10910      * @param {Number} index
10911      * @return {Roo.Element}
10912      */
10913     item : function(index){
10914         return this.elements[index] || null;
10915     },
10916
10917     /**
10918      * Returns the first Element
10919      * @return {Roo.Element}
10920      */
10921     first : function(){
10922         return this.item(0);
10923     },
10924
10925     /**
10926      * Returns the last Element
10927      * @return {Roo.Element}
10928      */
10929     last : function(){
10930         return this.item(this.elements.length-1);
10931     },
10932
10933     /**
10934      * Returns the number of elements in this composite
10935      * @return Number
10936      */
10937     getCount : function(){
10938         return this.elements.length;
10939     },
10940
10941     /**
10942      * Returns true if this composite contains the passed element
10943      * @return Boolean
10944      */
10945     contains : function(el){
10946         return this.indexOf(el) !== -1;
10947     },
10948
10949     /**
10950      * Returns true if this composite contains the passed element
10951      * @return Boolean
10952      */
10953     indexOf : function(el){
10954         return this.elements.indexOf(Roo.get(el));
10955     },
10956
10957
10958     /**
10959     * Removes the specified element(s).
10960     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10961     * or an array of any of those.
10962     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10963     * @return {CompositeElement} this
10964     */
10965     removeElement : function(el, removeDom){
10966         if(el instanceof Array){
10967             for(var i = 0, len = el.length; i < len; i++){
10968                 this.removeElement(el[i]);
10969             }
10970             return this;
10971         }
10972         var index = typeof el == 'number' ? el : this.indexOf(el);
10973         if(index !== -1){
10974             if(removeDom){
10975                 var d = this.elements[index];
10976                 if(d.dom){
10977                     d.remove();
10978                 }else{
10979                     d.parentNode.removeChild(d);
10980                 }
10981             }
10982             this.elements.splice(index, 1);
10983         }
10984         return this;
10985     },
10986
10987     /**
10988     * Replaces the specified element with the passed element.
10989     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10990     * to replace.
10991     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10992     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10993     * @return {CompositeElement} this
10994     */
10995     replaceElement : function(el, replacement, domReplace){
10996         var index = typeof el == 'number' ? el : this.indexOf(el);
10997         if(index !== -1){
10998             if(domReplace){
10999                 this.elements[index].replaceWith(replacement);
11000             }else{
11001                 this.elements.splice(index, 1, Roo.get(replacement))
11002             }
11003         }
11004         return this;
11005     },
11006
11007     /**
11008      * Removes all elements.
11009      */
11010     clear : function(){
11011         this.elements = [];
11012     }
11013 };
11014 (function(){
11015     Roo.CompositeElement.createCall = function(proto, fnName){
11016         if(!proto[fnName]){
11017             proto[fnName] = function(){
11018                 return this.invoke(fnName, arguments);
11019             };
11020         }
11021     };
11022     for(var fnName in Roo.Element.prototype){
11023         if(typeof Roo.Element.prototype[fnName] == "function"){
11024             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11025         }
11026     };
11027 })();
11028 /*
11029  * Based on:
11030  * Ext JS Library 1.1.1
11031  * Copyright(c) 2006-2007, Ext JS, LLC.
11032  *
11033  * Originally Released Under LGPL - original licence link has changed is not relivant.
11034  *
11035  * Fork - LGPL
11036  * <script type="text/javascript">
11037  */
11038
11039 /**
11040  * @class Roo.CompositeElementLite
11041  * @extends Roo.CompositeElement
11042  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11043  <pre><code>
11044  var els = Roo.select("#some-el div.some-class");
11045  // or select directly from an existing element
11046  var el = Roo.get('some-el');
11047  el.select('div.some-class');
11048
11049  els.setWidth(100); // all elements become 100 width
11050  els.hide(true); // all elements fade out and hide
11051  // or
11052  els.setWidth(100).hide(true);
11053  </code></pre><br><br>
11054  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11055  * actions will be performed on all the elements in this collection.</b>
11056  */
11057 Roo.CompositeElementLite = function(els){
11058     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11059     this.el = new Roo.Element.Flyweight();
11060 };
11061 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11062     addElements : function(els){
11063         if(els){
11064             if(els instanceof Array){
11065                 this.elements = this.elements.concat(els);
11066             }else{
11067                 var yels = this.elements;
11068                 var index = yels.length-1;
11069                 for(var i = 0, len = els.length; i < len; i++) {
11070                     yels[++index] = els[i];
11071                 }
11072             }
11073         }
11074         return this;
11075     },
11076     invoke : function(fn, args){
11077         var els = this.elements;
11078         var el = this.el;
11079         for(var i = 0, len = els.length; i < len; i++) {
11080             el.dom = els[i];
11081                 Roo.Element.prototype[fn].apply(el, args);
11082         }
11083         return this;
11084     },
11085     /**
11086      * Returns a flyweight Element of the dom element object at the specified index
11087      * @param {Number} index
11088      * @return {Roo.Element}
11089      */
11090     item : function(index){
11091         if(!this.elements[index]){
11092             return null;
11093         }
11094         this.el.dom = this.elements[index];
11095         return this.el;
11096     },
11097
11098     // fixes scope with flyweight
11099     addListener : function(eventName, handler, scope, opt){
11100         var els = this.elements;
11101         for(var i = 0, len = els.length; i < len; i++) {
11102             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11103         }
11104         return this;
11105     },
11106
11107     /**
11108     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11109     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11110     * a reference to the dom node, use el.dom.</b>
11111     * @param {Function} fn The function to call
11112     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11113     * @return {CompositeElement} this
11114     */
11115     each : function(fn, scope){
11116         var els = this.elements;
11117         var el = this.el;
11118         for(var i = 0, len = els.length; i < len; i++){
11119             el.dom = els[i];
11120                 if(fn.call(scope || el, el, this, i) === false){
11121                 break;
11122             }
11123         }
11124         return this;
11125     },
11126
11127     indexOf : function(el){
11128         return this.elements.indexOf(Roo.getDom(el));
11129     },
11130
11131     replaceElement : function(el, replacement, domReplace){
11132         var index = typeof el == 'number' ? el : this.indexOf(el);
11133         if(index !== -1){
11134             replacement = Roo.getDom(replacement);
11135             if(domReplace){
11136                 var d = this.elements[index];
11137                 d.parentNode.insertBefore(replacement, d);
11138                 d.parentNode.removeChild(d);
11139             }
11140             this.elements.splice(index, 1, replacement);
11141         }
11142         return this;
11143     }
11144 });
11145 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11146
11147 /*
11148  * Based on:
11149  * Ext JS Library 1.1.1
11150  * Copyright(c) 2006-2007, Ext JS, LLC.
11151  *
11152  * Originally Released Under LGPL - original licence link has changed is not relivant.
11153  *
11154  * Fork - LGPL
11155  * <script type="text/javascript">
11156  */
11157
11158  
11159
11160 /**
11161  * @class Roo.data.Connection
11162  * @extends Roo.util.Observable
11163  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11164  * either to a configured URL, or to a URL specified at request time.<br><br>
11165  * <p>
11166  * Requests made by this class are asynchronous, and will return immediately. No data from
11167  * the server will be available to the statement immediately following the {@link #request} call.
11168  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11169  * <p>
11170  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11171  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11172  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11173  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11174  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11175  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11176  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11177  * standard DOM methods.
11178  * @constructor
11179  * @param {Object} config a configuration object.
11180  */
11181 Roo.data.Connection = function(config){
11182     Roo.apply(this, config);
11183     this.addEvents({
11184         /**
11185          * @event beforerequest
11186          * Fires before a network request is made to retrieve a data object.
11187          * @param {Connection} conn This Connection object.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "beforerequest" : true,
11191         /**
11192          * @event requestcomplete
11193          * Fires if the request was successfully completed.
11194          * @param {Connection} conn This Connection object.
11195          * @param {Object} response The XHR object containing the response data.
11196          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11197          * @param {Object} options The options config object passed to the {@link #request} method.
11198          */
11199         "requestcomplete" : true,
11200         /**
11201          * @event requestexception
11202          * Fires if an error HTTP status was returned from the server.
11203          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11204          * @param {Connection} conn This Connection object.
11205          * @param {Object} response The XHR object containing the response data.
11206          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11207          * @param {Object} options The options config object passed to the {@link #request} method.
11208          */
11209         "requestexception" : true
11210     });
11211     Roo.data.Connection.superclass.constructor.call(this);
11212 };
11213
11214 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11215     /**
11216      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11217      */
11218     /**
11219      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11220      * extra parameters to each request made by this object. (defaults to undefined)
11221      */
11222     /**
11223      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11224      *  to each request made by this object. (defaults to undefined)
11225      */
11226     /**
11227      * @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)
11228      */
11229     /**
11230      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11231      */
11232     timeout : 30000,
11233     /**
11234      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11235      * @type Boolean
11236      */
11237     autoAbort:false,
11238
11239     /**
11240      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11241      * @type Boolean
11242      */
11243     disableCaching: true,
11244
11245     /**
11246      * Sends an HTTP request to a remote server.
11247      * @param {Object} options An object which may contain the following properties:<ul>
11248      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11249      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11250      * request, a url encoded string or a function to call to get either.</li>
11251      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11252      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11253      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11254      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11255      * <li>options {Object} The parameter to the request call.</li>
11256      * <li>success {Boolean} True if the request succeeded.</li>
11257      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11258      * </ul></li>
11259      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11260      * The callback is passed the following parameters:<ul>
11261      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11262      * <li>options {Object} The parameter to the request call.</li>
11263      * </ul></li>
11264      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11265      * The callback is passed the following parameters:<ul>
11266      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11267      * <li>options {Object} The parameter to the request call.</li>
11268      * </ul></li>
11269      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11270      * for the callback function. Defaults to the browser window.</li>
11271      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11272      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11273      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11274      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11275      * params for the post data. Any params will be appended to the URL.</li>
11276      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11277      * </ul>
11278      * @return {Number} transactionId
11279      */
11280     request : function(o){
11281         if(this.fireEvent("beforerequest", this, o) !== false){
11282             var p = o.params;
11283
11284             if(typeof p == "function"){
11285                 p = p.call(o.scope||window, o);
11286             }
11287             if(typeof p == "object"){
11288                 p = Roo.urlEncode(o.params);
11289             }
11290             if(this.extraParams){
11291                 var extras = Roo.urlEncode(this.extraParams);
11292                 p = p ? (p + '&' + extras) : extras;
11293             }
11294
11295             var url = o.url || this.url;
11296             if(typeof url == 'function'){
11297                 url = url.call(o.scope||window, o);
11298             }
11299
11300             if(o.form){
11301                 var form = Roo.getDom(o.form);
11302                 url = url || form.action;
11303
11304                 var enctype = form.getAttribute("enctype");
11305                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11306                     return this.doFormUpload(o, p, url);
11307                 }
11308                 var f = Roo.lib.Ajax.serializeForm(form);
11309                 p = p ? (p + '&' + f) : f;
11310             }
11311
11312             var hs = o.headers;
11313             if(this.defaultHeaders){
11314                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11315                 if(!o.headers){
11316                     o.headers = hs;
11317                 }
11318             }
11319
11320             var cb = {
11321                 success: this.handleResponse,
11322                 failure: this.handleFailure,
11323                 scope: this,
11324                 argument: {options: o},
11325                 timeout : this.timeout
11326             };
11327
11328             var method = o.method||this.method||(p ? "POST" : "GET");
11329
11330             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11331                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11332             }
11333
11334             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11335                 if(o.autoAbort){
11336                     this.abort();
11337                 }
11338             }else if(this.autoAbort !== false){
11339                 this.abort();
11340             }
11341
11342             if((method == 'GET' && p) || o.xmlData){
11343                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11344                 p = '';
11345             }
11346             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11347             return this.transId;
11348         }else{
11349             Roo.callback(o.callback, o.scope, [o, null, null]);
11350             return null;
11351         }
11352     },
11353
11354     /**
11355      * Determine whether this object has a request outstanding.
11356      * @param {Number} transactionId (Optional) defaults to the last transaction
11357      * @return {Boolean} True if there is an outstanding request.
11358      */
11359     isLoading : function(transId){
11360         if(transId){
11361             return Roo.lib.Ajax.isCallInProgress(transId);
11362         }else{
11363             return this.transId ? true : false;
11364         }
11365     },
11366
11367     /**
11368      * Aborts any outstanding request.
11369      * @param {Number} transactionId (Optional) defaults to the last transaction
11370      */
11371     abort : function(transId){
11372         if(transId || this.isLoading()){
11373             Roo.lib.Ajax.abort(transId || this.transId);
11374         }
11375     },
11376
11377     // private
11378     handleResponse : function(response){
11379         this.transId = false;
11380         var options = response.argument.options;
11381         response.argument = options ? options.argument : null;
11382         this.fireEvent("requestcomplete", this, response, options);
11383         Roo.callback(options.success, options.scope, [response, options]);
11384         Roo.callback(options.callback, options.scope, [options, true, response]);
11385     },
11386
11387     // private
11388     handleFailure : function(response, e){
11389         this.transId = false;
11390         var options = response.argument.options;
11391         response.argument = options ? options.argument : null;
11392         this.fireEvent("requestexception", this, response, options, e);
11393         Roo.callback(options.failure, options.scope, [response, options]);
11394         Roo.callback(options.callback, options.scope, [options, false, response]);
11395     },
11396
11397     // private
11398     doFormUpload : function(o, ps, url){
11399         var id = Roo.id();
11400         var frame = document.createElement('iframe');
11401         frame.id = id;
11402         frame.name = id;
11403         frame.className = 'x-hidden';
11404         if(Roo.isIE){
11405             frame.src = Roo.SSL_SECURE_URL;
11406         }
11407         document.body.appendChild(frame);
11408
11409         if(Roo.isIE){
11410            document.frames[id].name = id;
11411         }
11412
11413         var form = Roo.getDom(o.form);
11414         form.target = id;
11415         form.method = 'POST';
11416         form.enctype = form.encoding = 'multipart/form-data';
11417         if(url){
11418             form.action = url;
11419         }
11420
11421         var hiddens, hd;
11422         if(ps){ // add dynamic params
11423             hiddens = [];
11424             ps = Roo.urlDecode(ps, false);
11425             for(var k in ps){
11426                 if(ps.hasOwnProperty(k)){
11427                     hd = document.createElement('input');
11428                     hd.type = 'hidden';
11429                     hd.name = k;
11430                     hd.value = ps[k];
11431                     form.appendChild(hd);
11432                     hiddens.push(hd);
11433                 }
11434             }
11435         }
11436
11437         function cb(){
11438             var r = {  // bogus response object
11439                 responseText : '',
11440                 responseXML : null
11441             };
11442
11443             r.argument = o ? o.argument : null;
11444
11445             try { //
11446                 var doc;
11447                 if(Roo.isIE){
11448                     doc = frame.contentWindow.document;
11449                 }else {
11450                     doc = (frame.contentDocument || window.frames[id].document);
11451                 }
11452                 if(doc && doc.body){
11453                     r.responseText = doc.body.innerHTML;
11454                 }
11455                 if(doc && doc.XMLDocument){
11456                     r.responseXML = doc.XMLDocument;
11457                 }else {
11458                     r.responseXML = doc;
11459                 }
11460             }
11461             catch(e) {
11462                 // ignore
11463             }
11464
11465             Roo.EventManager.removeListener(frame, 'load', cb, this);
11466
11467             this.fireEvent("requestcomplete", this, r, o);
11468             Roo.callback(o.success, o.scope, [r, o]);
11469             Roo.callback(o.callback, o.scope, [o, true, r]);
11470
11471             setTimeout(function(){document.body.removeChild(frame);}, 100);
11472         }
11473
11474         Roo.EventManager.on(frame, 'load', cb, this);
11475         form.submit();
11476
11477         if(hiddens){ // remove dynamic params
11478             for(var i = 0, len = hiddens.length; i < len; i++){
11479                 form.removeChild(hiddens[i]);
11480             }
11481         }
11482     }
11483 });
11484
11485 /**
11486  * @class Roo.Ajax
11487  * @extends Roo.data.Connection
11488  * Global Ajax request class.
11489  *
11490  * @singleton
11491  */
11492 Roo.Ajax = new Roo.data.Connection({
11493     // fix up the docs
11494    /**
11495      * @cfg {String} url @hide
11496      */
11497     /**
11498      * @cfg {Object} extraParams @hide
11499      */
11500     /**
11501      * @cfg {Object} defaultHeaders @hide
11502      */
11503     /**
11504      * @cfg {String} method (Optional) @hide
11505      */
11506     /**
11507      * @cfg {Number} timeout (Optional) @hide
11508      */
11509     /**
11510      * @cfg {Boolean} autoAbort (Optional) @hide
11511      */
11512
11513     /**
11514      * @cfg {Boolean} disableCaching (Optional) @hide
11515      */
11516
11517     /**
11518      * @property  disableCaching
11519      * True to add a unique cache-buster param to GET requests. (defaults to true)
11520      * @type Boolean
11521      */
11522     /**
11523      * @property  url
11524      * The default URL to be used for requests to the server. (defaults to undefined)
11525      * @type String
11526      */
11527     /**
11528      * @property  extraParams
11529      * An object containing properties which are used as
11530      * extra parameters to each request made by this object. (defaults to undefined)
11531      * @type Object
11532      */
11533     /**
11534      * @property  defaultHeaders
11535      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11536      * @type Object
11537      */
11538     /**
11539      * @property  method
11540      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11541      * @type String
11542      */
11543     /**
11544      * @property  timeout
11545      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11546      * @type Number
11547      */
11548
11549     /**
11550      * @property  autoAbort
11551      * Whether a new request should abort any pending requests. (defaults to false)
11552      * @type Boolean
11553      */
11554     autoAbort : false,
11555
11556     /**
11557      * Serialize the passed form into a url encoded string
11558      * @param {String/HTMLElement} form
11559      * @return {String}
11560      */
11561     serializeForm : function(form){
11562         return Roo.lib.Ajax.serializeForm(form);
11563     }
11564 });/*
11565  * Based on:
11566  * Ext JS Library 1.1.1
11567  * Copyright(c) 2006-2007, Ext JS, LLC.
11568  *
11569  * Originally Released Under LGPL - original licence link has changed is not relivant.
11570  *
11571  * Fork - LGPL
11572  * <script type="text/javascript">
11573  */
11574  
11575 /**
11576  * Global Ajax request class.
11577  * 
11578  * @class Roo.Ajax
11579  * @extends Roo.data.Connection
11580  * @static
11581  * 
11582  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11583  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11584  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11585  * @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)
11586  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11587  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11588  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11589  */
11590 Roo.Ajax = new Roo.data.Connection({
11591     // fix up the docs
11592     /**
11593      * @scope Roo.Ajax
11594      * @type {Boolear} 
11595      */
11596     autoAbort : false,
11597
11598     /**
11599      * Serialize the passed form into a url encoded string
11600      * @scope Roo.Ajax
11601      * @param {String/HTMLElement} form
11602      * @return {String}
11603      */
11604     serializeForm : function(form){
11605         return Roo.lib.Ajax.serializeForm(form);
11606     }
11607 });/*
11608  * Based on:
11609  * Ext JS Library 1.1.1
11610  * Copyright(c) 2006-2007, Ext JS, LLC.
11611  *
11612  * Originally Released Under LGPL - original licence link has changed is not relivant.
11613  *
11614  * Fork - LGPL
11615  * <script type="text/javascript">
11616  */
11617
11618  
11619 /**
11620  * @class Roo.UpdateManager
11621  * @extends Roo.util.Observable
11622  * Provides AJAX-style update for Element object.<br><br>
11623  * Usage:<br>
11624  * <pre><code>
11625  * // Get it from a Roo.Element object
11626  * var el = Roo.get("foo");
11627  * var mgr = el.getUpdateManager();
11628  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11629  * ...
11630  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11631  * <br>
11632  * // or directly (returns the same UpdateManager instance)
11633  * var mgr = new Roo.UpdateManager("myElementId");
11634  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11635  * mgr.on("update", myFcnNeedsToKnow);
11636  * <br>
11637    // short handed call directly from the element object
11638    Roo.get("foo").load({
11639         url: "bar.php",
11640         scripts:true,
11641         params: "for=bar",
11642         text: "Loading Foo..."
11643    });
11644  * </code></pre>
11645  * @constructor
11646  * Create new UpdateManager directly.
11647  * @param {String/HTMLElement/Roo.Element} el The element to update
11648  * @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).
11649  */
11650 Roo.UpdateManager = function(el, forceNew){
11651     el = Roo.get(el);
11652     if(!forceNew && el.updateManager){
11653         return el.updateManager;
11654     }
11655     /**
11656      * The Element object
11657      * @type Roo.Element
11658      */
11659     this.el = el;
11660     /**
11661      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11662      * @type String
11663      */
11664     this.defaultUrl = null;
11665
11666     this.addEvents({
11667         /**
11668          * @event beforeupdate
11669          * Fired before an update is made, return false from your handler and the update is cancelled.
11670          * @param {Roo.Element} el
11671          * @param {String/Object/Function} url
11672          * @param {String/Object} params
11673          */
11674         "beforeupdate": true,
11675         /**
11676          * @event update
11677          * Fired after successful update is made.
11678          * @param {Roo.Element} el
11679          * @param {Object} oResponseObject The response Object
11680          */
11681         "update": true,
11682         /**
11683          * @event failure
11684          * Fired on update failure.
11685          * @param {Roo.Element} el
11686          * @param {Object} oResponseObject The response Object
11687          */
11688         "failure": true
11689     });
11690     var d = Roo.UpdateManager.defaults;
11691     /**
11692      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11693      * @type String
11694      */
11695     this.sslBlankUrl = d.sslBlankUrl;
11696     /**
11697      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11698      * @type Boolean
11699      */
11700     this.disableCaching = d.disableCaching;
11701     /**
11702      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11703      * @type String
11704      */
11705     this.indicatorText = d.indicatorText;
11706     /**
11707      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11708      * @type String
11709      */
11710     this.showLoadIndicator = d.showLoadIndicator;
11711     /**
11712      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11713      * @type Number
11714      */
11715     this.timeout = d.timeout;
11716
11717     /**
11718      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11719      * @type Boolean
11720      */
11721     this.loadScripts = d.loadScripts;
11722
11723     /**
11724      * Transaction object of current executing transaction
11725      */
11726     this.transaction = null;
11727
11728     /**
11729      * @private
11730      */
11731     this.autoRefreshProcId = null;
11732     /**
11733      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11734      * @type Function
11735      */
11736     this.refreshDelegate = this.refresh.createDelegate(this);
11737     /**
11738      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11739      * @type Function
11740      */
11741     this.updateDelegate = this.update.createDelegate(this);
11742     /**
11743      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11744      * @type Function
11745      */
11746     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11747     /**
11748      * @private
11749      */
11750     this.successDelegate = this.processSuccess.createDelegate(this);
11751     /**
11752      * @private
11753      */
11754     this.failureDelegate = this.processFailure.createDelegate(this);
11755
11756     if(!this.renderer){
11757      /**
11758       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11759       */
11760     this.renderer = new Roo.UpdateManager.BasicRenderer();
11761     }
11762     
11763     Roo.UpdateManager.superclass.constructor.call(this);
11764 };
11765
11766 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11767     /**
11768      * Get the Element this UpdateManager is bound to
11769      * @return {Roo.Element} The element
11770      */
11771     getEl : function(){
11772         return this.el;
11773     },
11774     /**
11775      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11776      * @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:
11777 <pre><code>
11778 um.update({<br/>
11779     url: "your-url.php",<br/>
11780     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11781     callback: yourFunction,<br/>
11782     scope: yourObject, //(optional scope)  <br/>
11783     discardUrl: false, <br/>
11784     nocache: false,<br/>
11785     text: "Loading...",<br/>
11786     timeout: 30,<br/>
11787     scripts: false<br/>
11788 });
11789 </code></pre>
11790      * The only required property is url. The optional properties nocache, text and scripts
11791      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11792      * @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}
11793      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11794      * @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.
11795      */
11796     update : function(url, params, callback, discardUrl){
11797         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11798             var method = this.method, cfg;
11799             if(typeof url == "object"){ // must be config object
11800                 cfg = url;
11801                 url = cfg.url;
11802                 params = params || cfg.params;
11803                 callback = callback || cfg.callback;
11804                 discardUrl = discardUrl || cfg.discardUrl;
11805                 if(callback && cfg.scope){
11806                     callback = callback.createDelegate(cfg.scope);
11807                 }
11808                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11809                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11810                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11811                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11812                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11813             }
11814             this.showLoading();
11815             if(!discardUrl){
11816                 this.defaultUrl = url;
11817             }
11818             if(typeof url == "function"){
11819                 url = url.call(this);
11820             }
11821
11822             method = method || (params ? "POST" : "GET");
11823             if(method == "GET"){
11824                 url = this.prepareUrl(url);
11825             }
11826
11827             var o = Roo.apply(cfg ||{}, {
11828                 url : url,
11829                 params: params,
11830                 success: this.successDelegate,
11831                 failure: this.failureDelegate,
11832                 callback: undefined,
11833                 timeout: (this.timeout*1000),
11834                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11835             });
11836
11837             this.transaction = Roo.Ajax.request(o);
11838         }
11839     },
11840
11841     /**
11842      * 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.
11843      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11844      * @param {String/HTMLElement} form The form Id or form element
11845      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11846      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11847      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11848      */
11849     formUpdate : function(form, url, reset, callback){
11850         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11851             if(typeof url == "function"){
11852                 url = url.call(this);
11853             }
11854             form = Roo.getDom(form);
11855             this.transaction = Roo.Ajax.request({
11856                 form: form,
11857                 url:url,
11858                 success: this.successDelegate,
11859                 failure: this.failureDelegate,
11860                 timeout: (this.timeout*1000),
11861                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11862             });
11863             this.showLoading.defer(1, this);
11864         }
11865     },
11866
11867     /**
11868      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11869      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11870      */
11871     refresh : function(callback){
11872         if(this.defaultUrl == null){
11873             return;
11874         }
11875         this.update(this.defaultUrl, null, callback, true);
11876     },
11877
11878     /**
11879      * Set this element to auto refresh.
11880      * @param {Number} interval How often to update (in seconds).
11881      * @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)
11882      * @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}
11883      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11884      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11885      */
11886     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11887         if(refreshNow){
11888             this.update(url || this.defaultUrl, params, callback, true);
11889         }
11890         if(this.autoRefreshProcId){
11891             clearInterval(this.autoRefreshProcId);
11892         }
11893         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11894     },
11895
11896     /**
11897      * Stop auto refresh on this element.
11898      */
11899      stopAutoRefresh : function(){
11900         if(this.autoRefreshProcId){
11901             clearInterval(this.autoRefreshProcId);
11902             delete this.autoRefreshProcId;
11903         }
11904     },
11905
11906     isAutoRefreshing : function(){
11907        return this.autoRefreshProcId ? true : false;
11908     },
11909     /**
11910      * Called to update the element to "Loading" state. Override to perform custom action.
11911      */
11912     showLoading : function(){
11913         if(this.showLoadIndicator){
11914             this.el.update(this.indicatorText);
11915         }
11916     },
11917
11918     /**
11919      * Adds unique parameter to query string if disableCaching = true
11920      * @private
11921      */
11922     prepareUrl : function(url){
11923         if(this.disableCaching){
11924             var append = "_dc=" + (new Date().getTime());
11925             if(url.indexOf("?") !== -1){
11926                 url += "&" + append;
11927             }else{
11928                 url += "?" + append;
11929             }
11930         }
11931         return url;
11932     },
11933
11934     /**
11935      * @private
11936      */
11937     processSuccess : function(response){
11938         this.transaction = null;
11939         if(response.argument.form && response.argument.reset){
11940             try{ // put in try/catch since some older FF releases had problems with this
11941                 response.argument.form.reset();
11942             }catch(e){}
11943         }
11944         if(this.loadScripts){
11945             this.renderer.render(this.el, response, this,
11946                 this.updateComplete.createDelegate(this, [response]));
11947         }else{
11948             this.renderer.render(this.el, response, this);
11949             this.updateComplete(response);
11950         }
11951     },
11952
11953     updateComplete : function(response){
11954         this.fireEvent("update", this.el, response);
11955         if(typeof response.argument.callback == "function"){
11956             response.argument.callback(this.el, true, response);
11957         }
11958     },
11959
11960     /**
11961      * @private
11962      */
11963     processFailure : function(response){
11964         this.transaction = null;
11965         this.fireEvent("failure", this.el, response);
11966         if(typeof response.argument.callback == "function"){
11967             response.argument.callback(this.el, false, response);
11968         }
11969     },
11970
11971     /**
11972      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11973      * @param {Object} renderer The object implementing the render() method
11974      */
11975     setRenderer : function(renderer){
11976         this.renderer = renderer;
11977     },
11978
11979     getRenderer : function(){
11980        return this.renderer;
11981     },
11982
11983     /**
11984      * Set the defaultUrl used for updates
11985      * @param {String/Function} defaultUrl The url or a function to call to get the url
11986      */
11987     setDefaultUrl : function(defaultUrl){
11988         this.defaultUrl = defaultUrl;
11989     },
11990
11991     /**
11992      * Aborts the executing transaction
11993      */
11994     abort : function(){
11995         if(this.transaction){
11996             Roo.Ajax.abort(this.transaction);
11997         }
11998     },
11999
12000     /**
12001      * Returns true if an update is in progress
12002      * @return {Boolean}
12003      */
12004     isUpdating : function(){
12005         if(this.transaction){
12006             return Roo.Ajax.isLoading(this.transaction);
12007         }
12008         return false;
12009     }
12010 });
12011
12012 /**
12013  * @class Roo.UpdateManager.defaults
12014  * @static (not really - but it helps the doc tool)
12015  * The defaults collection enables customizing the default properties of UpdateManager
12016  */
12017    Roo.UpdateManager.defaults = {
12018        /**
12019          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12020          * @type Number
12021          */
12022          timeout : 30,
12023
12024          /**
12025          * True to process scripts by default (Defaults to false).
12026          * @type Boolean
12027          */
12028         loadScripts : false,
12029
12030         /**
12031         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12032         * @type String
12033         */
12034         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12035         /**
12036          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12037          * @type Boolean
12038          */
12039         disableCaching : false,
12040         /**
12041          * Whether to show indicatorText when loading (Defaults to true).
12042          * @type Boolean
12043          */
12044         showLoadIndicator : true,
12045         /**
12046          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12047          * @type String
12048          */
12049         indicatorText : '<div class="loading-indicator">Loading...</div>'
12050    };
12051
12052 /**
12053  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12054  *Usage:
12055  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12056  * @param {String/HTMLElement/Roo.Element} el The element to update
12057  * @param {String} url The url
12058  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12059  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12060  * @static
12061  * @deprecated
12062  * @member Roo.UpdateManager
12063  */
12064 Roo.UpdateManager.updateElement = function(el, url, params, options){
12065     var um = Roo.get(el, true).getUpdateManager();
12066     Roo.apply(um, options);
12067     um.update(url, params, options ? options.callback : null);
12068 };
12069 // alias for backwards compat
12070 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12071 /**
12072  * @class Roo.UpdateManager.BasicRenderer
12073  * Default Content renderer. Updates the elements innerHTML with the responseText.
12074  */
12075 Roo.UpdateManager.BasicRenderer = function(){};
12076
12077 Roo.UpdateManager.BasicRenderer.prototype = {
12078     /**
12079      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12080      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12081      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12082      * @param {Roo.Element} el The element being rendered
12083      * @param {Object} response The YUI Connect response object
12084      * @param {UpdateManager} updateManager The calling update manager
12085      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12086      */
12087      render : function(el, response, updateManager, callback){
12088         el.update(response.responseText, updateManager.loadScripts, callback);
12089     }
12090 };
12091 /*
12092  * Based on:
12093  * Ext JS Library 1.1.1
12094  * Copyright(c) 2006-2007, Ext JS, LLC.
12095  *
12096  * Originally Released Under LGPL - original licence link has changed is not relivant.
12097  *
12098  * Fork - LGPL
12099  * <script type="text/javascript">
12100  */
12101
12102 /**
12103  * @class Roo.util.DelayedTask
12104  * Provides a convenient method of performing setTimeout where a new
12105  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12106  * You can use this class to buffer
12107  * the keypress events for a certain number of milliseconds, and perform only if they stop
12108  * for that amount of time.
12109  * @constructor The parameters to this constructor serve as defaults and are not required.
12110  * @param {Function} fn (optional) The default function to timeout
12111  * @param {Object} scope (optional) The default scope of that timeout
12112  * @param {Array} args (optional) The default Array of arguments
12113  */
12114 Roo.util.DelayedTask = function(fn, scope, args){
12115     var id = null, d, t;
12116
12117     var call = function(){
12118         var now = new Date().getTime();
12119         if(now - t >= d){
12120             clearInterval(id);
12121             id = null;
12122             fn.apply(scope, args || []);
12123         }
12124     };
12125     /**
12126      * Cancels any pending timeout and queues a new one
12127      * @param {Number} delay The milliseconds to delay
12128      * @param {Function} newFn (optional) Overrides function passed to constructor
12129      * @param {Object} newScope (optional) Overrides scope passed to constructor
12130      * @param {Array} newArgs (optional) Overrides args passed to constructor
12131      */
12132     this.delay = function(delay, newFn, newScope, newArgs){
12133         if(id && delay != d){
12134             this.cancel();
12135         }
12136         d = delay;
12137         t = new Date().getTime();
12138         fn = newFn || fn;
12139         scope = newScope || scope;
12140         args = newArgs || args;
12141         if(!id){
12142             id = setInterval(call, d);
12143         }
12144     };
12145
12146     /**
12147      * Cancel the last queued timeout
12148      */
12149     this.cancel = function(){
12150         if(id){
12151             clearInterval(id);
12152             id = null;
12153         }
12154     };
12155 };/*
12156  * Based on:
12157  * Ext JS Library 1.1.1
12158  * Copyright(c) 2006-2007, Ext JS, LLC.
12159  *
12160  * Originally Released Under LGPL - original licence link has changed is not relivant.
12161  *
12162  * Fork - LGPL
12163  * <script type="text/javascript">
12164  */
12165  
12166  
12167 Roo.util.TaskRunner = function(interval){
12168     interval = interval || 10;
12169     var tasks = [], removeQueue = [];
12170     var id = 0;
12171     var running = false;
12172
12173     var stopThread = function(){
12174         running = false;
12175         clearInterval(id);
12176         id = 0;
12177     };
12178
12179     var startThread = function(){
12180         if(!running){
12181             running = true;
12182             id = setInterval(runTasks, interval);
12183         }
12184     };
12185
12186     var removeTask = function(task){
12187         removeQueue.push(task);
12188         if(task.onStop){
12189             task.onStop();
12190         }
12191     };
12192
12193     var runTasks = function(){
12194         if(removeQueue.length > 0){
12195             for(var i = 0, len = removeQueue.length; i < len; i++){
12196                 tasks.remove(removeQueue[i]);
12197             }
12198             removeQueue = [];
12199             if(tasks.length < 1){
12200                 stopThread();
12201                 return;
12202             }
12203         }
12204         var now = new Date().getTime();
12205         for(var i = 0, len = tasks.length; i < len; ++i){
12206             var t = tasks[i];
12207             var itime = now - t.taskRunTime;
12208             if(t.interval <= itime){
12209                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12210                 t.taskRunTime = now;
12211                 if(rt === false || t.taskRunCount === t.repeat){
12212                     removeTask(t);
12213                     return;
12214                 }
12215             }
12216             if(t.duration && t.duration <= (now - t.taskStartTime)){
12217                 removeTask(t);
12218             }
12219         }
12220     };
12221
12222     /**
12223      * Queues a new task.
12224      * @param {Object} task
12225      */
12226     this.start = function(task){
12227         tasks.push(task);
12228         task.taskStartTime = new Date().getTime();
12229         task.taskRunTime = 0;
12230         task.taskRunCount = 0;
12231         startThread();
12232         return task;
12233     };
12234
12235     this.stop = function(task){
12236         removeTask(task);
12237         return task;
12238     };
12239
12240     this.stopAll = function(){
12241         stopThread();
12242         for(var i = 0, len = tasks.length; i < len; i++){
12243             if(tasks[i].onStop){
12244                 tasks[i].onStop();
12245             }
12246         }
12247         tasks = [];
12248         removeQueue = [];
12249     };
12250 };
12251
12252 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12253  * Based on:
12254  * Ext JS Library 1.1.1
12255  * Copyright(c) 2006-2007, Ext JS, LLC.
12256  *
12257  * Originally Released Under LGPL - original licence link has changed is not relivant.
12258  *
12259  * Fork - LGPL
12260  * <script type="text/javascript">
12261  */
12262
12263  
12264 /**
12265  * @class Roo.util.MixedCollection
12266  * @extends Roo.util.Observable
12267  * A Collection class that maintains both numeric indexes and keys and exposes events.
12268  * @constructor
12269  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12270  * collection (defaults to false)
12271  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12272  * and return the key value for that item.  This is used when available to look up the key on items that
12273  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12274  * equivalent to providing an implementation for the {@link #getKey} method.
12275  */
12276 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12277     this.items = [];
12278     this.map = {};
12279     this.keys = [];
12280     this.length = 0;
12281     this.addEvents({
12282         /**
12283          * @event clear
12284          * Fires when the collection is cleared.
12285          */
12286         "clear" : true,
12287         /**
12288          * @event add
12289          * Fires when an item is added to the collection.
12290          * @param {Number} index The index at which the item was added.
12291          * @param {Object} o The item added.
12292          * @param {String} key The key associated with the added item.
12293          */
12294         "add" : true,
12295         /**
12296          * @event replace
12297          * Fires when an item is replaced in the collection.
12298          * @param {String} key he key associated with the new added.
12299          * @param {Object} old The item being replaced.
12300          * @param {Object} new The new item.
12301          */
12302         "replace" : true,
12303         /**
12304          * @event remove
12305          * Fires when an item is removed from the collection.
12306          * @param {Object} o The item being removed.
12307          * @param {String} key (optional) The key associated with the removed item.
12308          */
12309         "remove" : true,
12310         "sort" : true
12311     });
12312     this.allowFunctions = allowFunctions === true;
12313     if(keyFn){
12314         this.getKey = keyFn;
12315     }
12316     Roo.util.MixedCollection.superclass.constructor.call(this);
12317 };
12318
12319 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12320     allowFunctions : false,
12321     
12322 /**
12323  * Adds an item to the collection.
12324  * @param {String} key The key to associate with the item
12325  * @param {Object} o The item to add.
12326  * @return {Object} The item added.
12327  */
12328     add : function(key, o){
12329         if(arguments.length == 1){
12330             o = arguments[0];
12331             key = this.getKey(o);
12332         }
12333         if(typeof key == "undefined" || key === null){
12334             this.length++;
12335             this.items.push(o);
12336             this.keys.push(null);
12337         }else{
12338             var old = this.map[key];
12339             if(old){
12340                 return this.replace(key, o);
12341             }
12342             this.length++;
12343             this.items.push(o);
12344             this.map[key] = o;
12345             this.keys.push(key);
12346         }
12347         this.fireEvent("add", this.length-1, o, key);
12348         return o;
12349     },
12350        
12351 /**
12352   * MixedCollection has a generic way to fetch keys if you implement getKey.
12353 <pre><code>
12354 // normal way
12355 var mc = new Roo.util.MixedCollection();
12356 mc.add(someEl.dom.id, someEl);
12357 mc.add(otherEl.dom.id, otherEl);
12358 //and so on
12359
12360 // using getKey
12361 var mc = new Roo.util.MixedCollection();
12362 mc.getKey = function(el){
12363    return el.dom.id;
12364 };
12365 mc.add(someEl);
12366 mc.add(otherEl);
12367
12368 // or via the constructor
12369 var mc = new Roo.util.MixedCollection(false, function(el){
12370    return el.dom.id;
12371 });
12372 mc.add(someEl);
12373 mc.add(otherEl);
12374 </code></pre>
12375  * @param o {Object} The item for which to find the key.
12376  * @return {Object} The key for the passed item.
12377  */
12378     getKey : function(o){
12379          return o.id; 
12380     },
12381    
12382 /**
12383  * Replaces an item in the collection.
12384  * @param {String} key The key associated with the item to replace, or the item to replace.
12385  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12386  * @return {Object}  The new item.
12387  */
12388     replace : function(key, o){
12389         if(arguments.length == 1){
12390             o = arguments[0];
12391             key = this.getKey(o);
12392         }
12393         var old = this.item(key);
12394         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12395              return this.add(key, o);
12396         }
12397         var index = this.indexOfKey(key);
12398         this.items[index] = o;
12399         this.map[key] = o;
12400         this.fireEvent("replace", key, old, o);
12401         return o;
12402     },
12403    
12404 /**
12405  * Adds all elements of an Array or an Object to the collection.
12406  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12407  * an Array of values, each of which are added to the collection.
12408  */
12409     addAll : function(objs){
12410         if(arguments.length > 1 || objs instanceof Array){
12411             var args = arguments.length > 1 ? arguments : objs;
12412             for(var i = 0, len = args.length; i < len; i++){
12413                 this.add(args[i]);
12414             }
12415         }else{
12416             for(var key in objs){
12417                 if(this.allowFunctions || typeof objs[key] != "function"){
12418                     this.add(key, objs[key]);
12419                 }
12420             }
12421         }
12422     },
12423    
12424 /**
12425  * Executes the specified function once for every item in the collection, passing each
12426  * item as the first and only parameter. returning false from the function will stop the iteration.
12427  * @param {Function} fn The function to execute for each item.
12428  * @param {Object} scope (optional) The scope in which to execute the function.
12429  */
12430     each : function(fn, scope){
12431         var items = [].concat(this.items); // each safe for removal
12432         for(var i = 0, len = items.length; i < len; i++){
12433             if(fn.call(scope || items[i], items[i], i, len) === false){
12434                 break;
12435             }
12436         }
12437     },
12438    
12439 /**
12440  * Executes the specified function once for every key in the collection, passing each
12441  * key, and its associated item as the first two parameters.
12442  * @param {Function} fn The function to execute for each item.
12443  * @param {Object} scope (optional) The scope in which to execute the function.
12444  */
12445     eachKey : function(fn, scope){
12446         for(var i = 0, len = this.keys.length; i < len; i++){
12447             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12448         }
12449     },
12450    
12451 /**
12452  * Returns the first item in the collection which elicits a true return value from the
12453  * passed selection function.
12454  * @param {Function} fn The selection function to execute for each item.
12455  * @param {Object} scope (optional) The scope in which to execute the function.
12456  * @return {Object} The first item in the collection which returned true from the selection function.
12457  */
12458     find : function(fn, scope){
12459         for(var i = 0, len = this.items.length; i < len; i++){
12460             if(fn.call(scope || window, this.items[i], this.keys[i])){
12461                 return this.items[i];
12462             }
12463         }
12464         return null;
12465     },
12466    
12467 /**
12468  * Inserts an item at the specified index in the collection.
12469  * @param {Number} index The index to insert the item at.
12470  * @param {String} key The key to associate with the new item, or the item itself.
12471  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12472  * @return {Object} The item inserted.
12473  */
12474     insert : function(index, key, o){
12475         if(arguments.length == 2){
12476             o = arguments[1];
12477             key = this.getKey(o);
12478         }
12479         if(index >= this.length){
12480             return this.add(key, o);
12481         }
12482         this.length++;
12483         this.items.splice(index, 0, o);
12484         if(typeof key != "undefined" && key != null){
12485             this.map[key] = o;
12486         }
12487         this.keys.splice(index, 0, key);
12488         this.fireEvent("add", index, o, key);
12489         return o;
12490     },
12491    
12492 /**
12493  * Removed an item from the collection.
12494  * @param {Object} o The item to remove.
12495  * @return {Object} The item removed.
12496  */
12497     remove : function(o){
12498         return this.removeAt(this.indexOf(o));
12499     },
12500    
12501 /**
12502  * Remove an item from a specified index in the collection.
12503  * @param {Number} index The index within the collection of the item to remove.
12504  */
12505     removeAt : function(index){
12506         if(index < this.length && index >= 0){
12507             this.length--;
12508             var o = this.items[index];
12509             this.items.splice(index, 1);
12510             var key = this.keys[index];
12511             if(typeof key != "undefined"){
12512                 delete this.map[key];
12513             }
12514             this.keys.splice(index, 1);
12515             this.fireEvent("remove", o, key);
12516         }
12517     },
12518    
12519 /**
12520  * Removed an item associated with the passed key fom the collection.
12521  * @param {String} key The key of the item to remove.
12522  */
12523     removeKey : function(key){
12524         return this.removeAt(this.indexOfKey(key));
12525     },
12526    
12527 /**
12528  * Returns the number of items in the collection.
12529  * @return {Number} the number of items in the collection.
12530  */
12531     getCount : function(){
12532         return this.length; 
12533     },
12534    
12535 /**
12536  * Returns index within the collection of the passed Object.
12537  * @param {Object} o The item to find the index of.
12538  * @return {Number} index of the item.
12539  */
12540     indexOf : function(o){
12541         if(!this.items.indexOf){
12542             for(var i = 0, len = this.items.length; i < len; i++){
12543                 if(this.items[i] == o) return i;
12544             }
12545             return -1;
12546         }else{
12547             return this.items.indexOf(o);
12548         }
12549     },
12550    
12551 /**
12552  * Returns index within the collection of the passed key.
12553  * @param {String} key The key to find the index of.
12554  * @return {Number} index of the key.
12555  */
12556     indexOfKey : function(key){
12557         if(!this.keys.indexOf){
12558             for(var i = 0, len = this.keys.length; i < len; i++){
12559                 if(this.keys[i] == key) return i;
12560             }
12561             return -1;
12562         }else{
12563             return this.keys.indexOf(key);
12564         }
12565     },
12566    
12567 /**
12568  * Returns the item associated with the passed key OR index. Key has priority over index.
12569  * @param {String/Number} key The key or index of the item.
12570  * @return {Object} The item associated with the passed key.
12571  */
12572     item : function(key){
12573         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12574         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12575     },
12576     
12577 /**
12578  * Returns the item at the specified index.
12579  * @param {Number} index The index of the item.
12580  * @return {Object}
12581  */
12582     itemAt : function(index){
12583         return this.items[index];
12584     },
12585     
12586 /**
12587  * Returns the item associated with the passed key.
12588  * @param {String/Number} key The key of the item.
12589  * @return {Object} The item associated with the passed key.
12590  */
12591     key : function(key){
12592         return this.map[key];
12593     },
12594    
12595 /**
12596  * Returns true if the collection contains the passed Object as an item.
12597  * @param {Object} o  The Object to look for in the collection.
12598  * @return {Boolean} True if the collection contains the Object as an item.
12599  */
12600     contains : function(o){
12601         return this.indexOf(o) != -1;
12602     },
12603    
12604 /**
12605  * Returns true if the collection contains the passed Object as a key.
12606  * @param {String} key The key to look for in the collection.
12607  * @return {Boolean} True if the collection contains the Object as a key.
12608  */
12609     containsKey : function(key){
12610         return typeof this.map[key] != "undefined";
12611     },
12612    
12613 /**
12614  * Removes all items from the collection.
12615  */
12616     clear : function(){
12617         this.length = 0;
12618         this.items = [];
12619         this.keys = [];
12620         this.map = {};
12621         this.fireEvent("clear");
12622     },
12623    
12624 /**
12625  * Returns the first item in the collection.
12626  * @return {Object} the first item in the collection..
12627  */
12628     first : function(){
12629         return this.items[0]; 
12630     },
12631    
12632 /**
12633  * Returns the last item in the collection.
12634  * @return {Object} the last item in the collection..
12635  */
12636     last : function(){
12637         return this.items[this.length-1];   
12638     },
12639     
12640     _sort : function(property, dir, fn){
12641         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12642         fn = fn || function(a, b){
12643             return a-b;
12644         };
12645         var c = [], k = this.keys, items = this.items;
12646         for(var i = 0, len = items.length; i < len; i++){
12647             c[c.length] = {key: k[i], value: items[i], index: i};
12648         }
12649         c.sort(function(a, b){
12650             var v = fn(a[property], b[property]) * dsc;
12651             if(v == 0){
12652                 v = (a.index < b.index ? -1 : 1);
12653             }
12654             return v;
12655         });
12656         for(var i = 0, len = c.length; i < len; i++){
12657             items[i] = c[i].value;
12658             k[i] = c[i].key;
12659         }
12660         this.fireEvent("sort", this);
12661     },
12662     
12663     /**
12664      * Sorts this collection with the passed comparison function
12665      * @param {String} direction (optional) "ASC" or "DESC"
12666      * @param {Function} fn (optional) comparison function
12667      */
12668     sort : function(dir, fn){
12669         this._sort("value", dir, fn);
12670     },
12671     
12672     /**
12673      * Sorts this collection by keys
12674      * @param {String} direction (optional) "ASC" or "DESC"
12675      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12676      */
12677     keySort : function(dir, fn){
12678         this._sort("key", dir, fn || function(a, b){
12679             return String(a).toUpperCase()-String(b).toUpperCase();
12680         });
12681     },
12682     
12683     /**
12684      * Returns a range of items in this collection
12685      * @param {Number} startIndex (optional) defaults to 0
12686      * @param {Number} endIndex (optional) default to the last item
12687      * @return {Array} An array of items
12688      */
12689     getRange : function(start, end){
12690         var items = this.items;
12691         if(items.length < 1){
12692             return [];
12693         }
12694         start = start || 0;
12695         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12696         var r = [];
12697         if(start <= end){
12698             for(var i = start; i <= end; i++) {
12699                     r[r.length] = items[i];
12700             }
12701         }else{
12702             for(var i = start; i >= end; i--) {
12703                     r[r.length] = items[i];
12704             }
12705         }
12706         return r;
12707     },
12708         
12709     /**
12710      * Filter the <i>objects</i> in this collection by a specific property. 
12711      * Returns a new collection that has been filtered.
12712      * @param {String} property A property on your objects
12713      * @param {String/RegExp} value Either string that the property values 
12714      * should start with or a RegExp to test against the property
12715      * @return {MixedCollection} The new filtered collection
12716      */
12717     filter : function(property, value){
12718         if(!value.exec){ // not a regex
12719             value = String(value);
12720             if(value.length == 0){
12721                 return this.clone();
12722             }
12723             value = new RegExp("^" + Roo.escapeRe(value), "i");
12724         }
12725         return this.filterBy(function(o){
12726             return o && value.test(o[property]);
12727         });
12728         },
12729     
12730     /**
12731      * Filter by a function. * Returns a new collection that has been filtered.
12732      * The passed function will be called with each 
12733      * object in the collection. If the function returns true, the value is included 
12734      * otherwise it is filtered.
12735      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12736      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12737      * @return {MixedCollection} The new filtered collection
12738      */
12739     filterBy : function(fn, scope){
12740         var r = new Roo.util.MixedCollection();
12741         r.getKey = this.getKey;
12742         var k = this.keys, it = this.items;
12743         for(var i = 0, len = it.length; i < len; i++){
12744             if(fn.call(scope||this, it[i], k[i])){
12745                                 r.add(k[i], it[i]);
12746                         }
12747         }
12748         return r;
12749     },
12750     
12751     /**
12752      * Creates a duplicate of this collection
12753      * @return {MixedCollection}
12754      */
12755     clone : function(){
12756         var r = new Roo.util.MixedCollection();
12757         var k = this.keys, it = this.items;
12758         for(var i = 0, len = it.length; i < len; i++){
12759             r.add(k[i], it[i]);
12760         }
12761         r.getKey = this.getKey;
12762         return r;
12763     }
12764 });
12765 /**
12766  * Returns the item associated with the passed key or index.
12767  * @method
12768  * @param {String/Number} key The key or index of the item.
12769  * @return {Object} The item associated with the passed key.
12770  */
12771 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12772  * Based on:
12773  * Ext JS Library 1.1.1
12774  * Copyright(c) 2006-2007, Ext JS, LLC.
12775  *
12776  * Originally Released Under LGPL - original licence link has changed is not relivant.
12777  *
12778  * Fork - LGPL
12779  * <script type="text/javascript">
12780  */
12781 /**
12782  * @class Roo.util.JSON
12783  * Modified version of Douglas Crockford"s json.js that doesn"t
12784  * mess with the Object prototype 
12785  * http://www.json.org/js.html
12786  * @singleton
12787  */
12788 Roo.util.JSON = new (function(){
12789     var useHasOwn = {}.hasOwnProperty ? true : false;
12790     
12791     // crashes Safari in some instances
12792     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12793     
12794     var pad = function(n) {
12795         return n < 10 ? "0" + n : n;
12796     };
12797     
12798     var m = {
12799         "\b": '\\b',
12800         "\t": '\\t',
12801         "\n": '\\n',
12802         "\f": '\\f',
12803         "\r": '\\r',
12804         '"' : '\\"',
12805         "\\": '\\\\'
12806     };
12807
12808     var encodeString = function(s){
12809         if (/["\\\x00-\x1f]/.test(s)) {
12810             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12811                 var c = m[b];
12812                 if(c){
12813                     return c;
12814                 }
12815                 c = b.charCodeAt();
12816                 return "\\u00" +
12817                     Math.floor(c / 16).toString(16) +
12818                     (c % 16).toString(16);
12819             }) + '"';
12820         }
12821         return '"' + s + '"';
12822     };
12823     
12824     var encodeArray = function(o){
12825         var a = ["["], b, i, l = o.length, v;
12826             for (i = 0; i < l; i += 1) {
12827                 v = o[i];
12828                 switch (typeof v) {
12829                     case "undefined":
12830                     case "function":
12831                     case "unknown":
12832                         break;
12833                     default:
12834                         if (b) {
12835                             a.push(',');
12836                         }
12837                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12838                         b = true;
12839                 }
12840             }
12841             a.push("]");
12842             return a.join("");
12843     };
12844     
12845     var encodeDate = function(o){
12846         return '"' + o.getFullYear() + "-" +
12847                 pad(o.getMonth() + 1) + "-" +
12848                 pad(o.getDate()) + "T" +
12849                 pad(o.getHours()) + ":" +
12850                 pad(o.getMinutes()) + ":" +
12851                 pad(o.getSeconds()) + '"';
12852     };
12853     
12854     /**
12855      * Encodes an Object, Array or other value
12856      * @param {Mixed} o The variable to encode
12857      * @return {String} The JSON string
12858      */
12859     this.encode = function(o)
12860     {
12861         // should this be extended to fully wrap stringify..
12862         
12863         if(typeof o == "undefined" || o === null){
12864             return "null";
12865         }else if(o instanceof Array){
12866             return encodeArray(o);
12867         }else if(o instanceof Date){
12868             return encodeDate(o);
12869         }else if(typeof o == "string"){
12870             return encodeString(o);
12871         }else if(typeof o == "number"){
12872             return isFinite(o) ? String(o) : "null";
12873         }else if(typeof o == "boolean"){
12874             return String(o);
12875         }else {
12876             var a = ["{"], b, i, v;
12877             for (i in o) {
12878                 if(!useHasOwn || o.hasOwnProperty(i)) {
12879                     v = o[i];
12880                     switch (typeof v) {
12881                     case "undefined":
12882                     case "function":
12883                     case "unknown":
12884                         break;
12885                     default:
12886                         if(b){
12887                             a.push(',');
12888                         }
12889                         a.push(this.encode(i), ":",
12890                                 v === null ? "null" : this.encode(v));
12891                         b = true;
12892                     }
12893                 }
12894             }
12895             a.push("}");
12896             return a.join("");
12897         }
12898     };
12899     
12900     /**
12901      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12902      * @param {String} json The JSON string
12903      * @return {Object} The resulting object
12904      */
12905     this.decode = function(json){
12906         
12907         return  /** eval:var:json */ eval("(" + json + ')');
12908     };
12909 })();
12910 /** 
12911  * Shorthand for {@link Roo.util.JSON#encode}
12912  * @member Roo encode 
12913  * @method */
12914 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12915 /** 
12916  * Shorthand for {@link Roo.util.JSON#decode}
12917  * @member Roo decode 
12918  * @method */
12919 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12920 /*
12921  * Based on:
12922  * Ext JS Library 1.1.1
12923  * Copyright(c) 2006-2007, Ext JS, LLC.
12924  *
12925  * Originally Released Under LGPL - original licence link has changed is not relivant.
12926  *
12927  * Fork - LGPL
12928  * <script type="text/javascript">
12929  */
12930  
12931 /**
12932  * @class Roo.util.Format
12933  * Reusable data formatting functions
12934  * @singleton
12935  */
12936 Roo.util.Format = function(){
12937     var trimRe = /^\s+|\s+$/g;
12938     return {
12939         /**
12940          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12941          * @param {String} value The string to truncate
12942          * @param {Number} length The maximum length to allow before truncating
12943          * @return {String} The converted text
12944          */
12945         ellipsis : function(value, len){
12946             if(value && value.length > len){
12947                 return value.substr(0, len-3)+"...";
12948             }
12949             return value;
12950         },
12951
12952         /**
12953          * Checks a reference and converts it to empty string if it is undefined
12954          * @param {Mixed} value Reference to check
12955          * @return {Mixed} Empty string if converted, otherwise the original value
12956          */
12957         undef : function(value){
12958             return typeof value != "undefined" ? value : "";
12959         },
12960
12961         /**
12962          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12963          * @param {String} value The string to encode
12964          * @return {String} The encoded text
12965          */
12966         htmlEncode : function(value){
12967             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12968         },
12969
12970         /**
12971          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12972          * @param {String} value The string to decode
12973          * @return {String} The decoded text
12974          */
12975         htmlDecode : function(value){
12976             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12977         },
12978
12979         /**
12980          * Trims any whitespace from either side of a string
12981          * @param {String} value The text to trim
12982          * @return {String} The trimmed text
12983          */
12984         trim : function(value){
12985             return String(value).replace(trimRe, "");
12986         },
12987
12988         /**
12989          * Returns a substring from within an original string
12990          * @param {String} value The original text
12991          * @param {Number} start The start index of the substring
12992          * @param {Number} length The length of the substring
12993          * @return {String} The substring
12994          */
12995         substr : function(value, start, length){
12996             return String(value).substr(start, length);
12997         },
12998
12999         /**
13000          * Converts a string to all lower case letters
13001          * @param {String} value The text to convert
13002          * @return {String} The converted text
13003          */
13004         lowercase : function(value){
13005             return String(value).toLowerCase();
13006         },
13007
13008         /**
13009          * Converts a string to all upper case letters
13010          * @param {String} value The text to convert
13011          * @return {String} The converted text
13012          */
13013         uppercase : function(value){
13014             return String(value).toUpperCase();
13015         },
13016
13017         /**
13018          * Converts the first character only of a string to upper case
13019          * @param {String} value The text to convert
13020          * @return {String} The converted text
13021          */
13022         capitalize : function(value){
13023             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13024         },
13025
13026         // private
13027         call : function(value, fn){
13028             if(arguments.length > 2){
13029                 var args = Array.prototype.slice.call(arguments, 2);
13030                 args.unshift(value);
13031                  
13032                 return /** eval:var:value */  eval(fn).apply(window, args);
13033             }else{
13034                 /** eval:var:value */
13035                 return /** eval:var:value */ eval(fn).call(window, value);
13036             }
13037         },
13038
13039        
13040         /**
13041          * safer version of Math.toFixed..??/
13042          * @param {Number/String} value The numeric value to format
13043          * @param {Number/String} value Decimal places 
13044          * @return {String} The formatted currency string
13045          */
13046         toFixed : function(v, n)
13047         {
13048             // why not use to fixed - precision is buggered???
13049             if (!n) {
13050                 return Math.round(v-0);
13051             }
13052             var fact = Math.pow(10,n+1);
13053             v = (Math.round((v-0)*fact))/fact;
13054             var z = (''+fact).substring(2);
13055             if (v == Math.floor(v)) {
13056                 return Math.floor(v) + '.' + z;
13057             }
13058             
13059             // now just padd decimals..
13060             var ps = String(v).split('.');
13061             var fd = (ps[1] + z);
13062             var r = fd.substring(0,n); 
13063             var rm = fd.substring(n); 
13064             if (rm < 5) {
13065                 return ps[0] + '.' + r;
13066             }
13067             r*=1; // turn it into a number;
13068             r++;
13069             if (String(r).length != n) {
13070                 ps[0]*=1;
13071                 ps[0]++;
13072                 r = String(r).substring(1); // chop the end off.
13073             }
13074             
13075             return ps[0] + '.' + r;
13076              
13077         },
13078         
13079         /**
13080          * Format a number as US currency
13081          * @param {Number/String} value The numeric value to format
13082          * @return {String} The formatted currency string
13083          */
13084         usMoney : function(v){
13085             v = (Math.round((v-0)*100))/100;
13086             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13087             v = String(v);
13088             var ps = v.split('.');
13089             var whole = ps[0];
13090             var sub = ps[1] ? '.'+ ps[1] : '.00';
13091             var r = /(\d+)(\d{3})/;
13092             while (r.test(whole)) {
13093                 whole = whole.replace(r, '$1' + ',' + '$2');
13094             }
13095             return "$" + whole + sub ;
13096         },
13097         
13098         /**
13099          * Parse a value into a formatted date using the specified format pattern.
13100          * @param {Mixed} value The value to format
13101          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13102          * @return {String} The formatted date string
13103          */
13104         date : function(v, format){
13105             if(!v){
13106                 return "";
13107             }
13108             if(!(v instanceof Date)){
13109                 v = new Date(Date.parse(v));
13110             }
13111             return v.dateFormat(format || "m/d/Y");
13112         },
13113
13114         /**
13115          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13116          * @param {String} format Any valid date format string
13117          * @return {Function} The date formatting function
13118          */
13119         dateRenderer : function(format){
13120             return function(v){
13121                 return Roo.util.Format.date(v, format);  
13122             };
13123         },
13124
13125         // private
13126         stripTagsRE : /<\/?[^>]+>/gi,
13127         
13128         /**
13129          * Strips all HTML tags
13130          * @param {Mixed} value The text from which to strip tags
13131          * @return {String} The stripped text
13132          */
13133         stripTags : function(v){
13134             return !v ? v : String(v).replace(this.stripTagsRE, "");
13135         }
13136     };
13137 }();/*
13138  * Based on:
13139  * Ext JS Library 1.1.1
13140  * Copyright(c) 2006-2007, Ext JS, LLC.
13141  *
13142  * Originally Released Under LGPL - original licence link has changed is not relivant.
13143  *
13144  * Fork - LGPL
13145  * <script type="text/javascript">
13146  */
13147
13148
13149  
13150
13151 /**
13152  * @class Roo.MasterTemplate
13153  * @extends Roo.Template
13154  * Provides a template that can have child templates. The syntax is:
13155 <pre><code>
13156 var t = new Roo.MasterTemplate(
13157         '&lt;select name="{name}"&gt;',
13158                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13159         '&lt;/select&gt;'
13160 );
13161 t.add('options', {value: 'foo', text: 'bar'});
13162 // or you can add multiple child elements in one shot
13163 t.addAll('options', [
13164     {value: 'foo', text: 'bar'},
13165     {value: 'foo2', text: 'bar2'},
13166     {value: 'foo3', text: 'bar3'}
13167 ]);
13168 // then append, applying the master template values
13169 t.append('my-form', {name: 'my-select'});
13170 </code></pre>
13171 * A name attribute for the child template is not required if you have only one child
13172 * template or you want to refer to them by index.
13173  */
13174 Roo.MasterTemplate = function(){
13175     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13176     this.originalHtml = this.html;
13177     var st = {};
13178     var m, re = this.subTemplateRe;
13179     re.lastIndex = 0;
13180     var subIndex = 0;
13181     while(m = re.exec(this.html)){
13182         var name = m[1], content = m[2];
13183         st[subIndex] = {
13184             name: name,
13185             index: subIndex,
13186             buffer: [],
13187             tpl : new Roo.Template(content)
13188         };
13189         if(name){
13190             st[name] = st[subIndex];
13191         }
13192         st[subIndex].tpl.compile();
13193         st[subIndex].tpl.call = this.call.createDelegate(this);
13194         subIndex++;
13195     }
13196     this.subCount = subIndex;
13197     this.subs = st;
13198 };
13199 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13200     /**
13201     * The regular expression used to match sub templates
13202     * @type RegExp
13203     * @property
13204     */
13205     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13206
13207     /**
13208      * Applies the passed values to a child template.
13209      * @param {String/Number} name (optional) The name or index of the child template
13210      * @param {Array/Object} values The values to be applied to the template
13211      * @return {MasterTemplate} this
13212      */
13213      add : function(name, values){
13214         if(arguments.length == 1){
13215             values = arguments[0];
13216             name = 0;
13217         }
13218         var s = this.subs[name];
13219         s.buffer[s.buffer.length] = s.tpl.apply(values);
13220         return this;
13221     },
13222
13223     /**
13224      * Applies all the passed values to a child template.
13225      * @param {String/Number} name (optional) The name or index of the child template
13226      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13227      * @param {Boolean} reset (optional) True to reset the template first
13228      * @return {MasterTemplate} this
13229      */
13230     fill : function(name, values, reset){
13231         var a = arguments;
13232         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13233             values = a[0];
13234             name = 0;
13235             reset = a[1];
13236         }
13237         if(reset){
13238             this.reset();
13239         }
13240         for(var i = 0, len = values.length; i < len; i++){
13241             this.add(name, values[i]);
13242         }
13243         return this;
13244     },
13245
13246     /**
13247      * Resets the template for reuse
13248      * @return {MasterTemplate} this
13249      */
13250      reset : function(){
13251         var s = this.subs;
13252         for(var i = 0; i < this.subCount; i++){
13253             s[i].buffer = [];
13254         }
13255         return this;
13256     },
13257
13258     applyTemplate : function(values){
13259         var s = this.subs;
13260         var replaceIndex = -1;
13261         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13262             return s[++replaceIndex].buffer.join("");
13263         });
13264         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13265     },
13266
13267     apply : function(){
13268         return this.applyTemplate.apply(this, arguments);
13269     },
13270
13271     compile : function(){return this;}
13272 });
13273
13274 /**
13275  * Alias for fill().
13276  * @method
13277  */
13278 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13279  /**
13280  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13281  * var tpl = Roo.MasterTemplate.from('element-id');
13282  * @param {String/HTMLElement} el
13283  * @param {Object} config
13284  * @static
13285  */
13286 Roo.MasterTemplate.from = function(el, config){
13287     el = Roo.getDom(el);
13288     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13289 };/*
13290  * Based on:
13291  * Ext JS Library 1.1.1
13292  * Copyright(c) 2006-2007, Ext JS, LLC.
13293  *
13294  * Originally Released Under LGPL - original licence link has changed is not relivant.
13295  *
13296  * Fork - LGPL
13297  * <script type="text/javascript">
13298  */
13299
13300  
13301 /**
13302  * @class Roo.util.CSS
13303  * Utility class for manipulating CSS rules
13304  * @singleton
13305  */
13306 Roo.util.CSS = function(){
13307         var rules = null;
13308         var doc = document;
13309
13310     var camelRe = /(-[a-z])/gi;
13311     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13312
13313    return {
13314    /**
13315     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13316     * tag and appended to the HEAD of the document.
13317     * @param {String|Object} cssText The text containing the css rules
13318     * @param {String} id An id to add to the stylesheet for later removal
13319     * @return {StyleSheet}
13320     */
13321     createStyleSheet : function(cssText, id){
13322         var ss;
13323         var head = doc.getElementsByTagName("head")[0];
13324         var nrules = doc.createElement("style");
13325         nrules.setAttribute("type", "text/css");
13326         if(id){
13327             nrules.setAttribute("id", id);
13328         }
13329         if (typeof(cssText) != 'string') {
13330             // support object maps..
13331             // not sure if this a good idea.. 
13332             // perhaps it should be merged with the general css handling
13333             // and handle js style props.
13334             var cssTextNew = [];
13335             for(var n in cssText) {
13336                 var citems = [];
13337                 for(var k in cssText[n]) {
13338                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13339                 }
13340                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13341                 
13342             }
13343             cssText = cssTextNew.join("\n");
13344             
13345         }
13346        
13347        
13348        if(Roo.isIE){
13349            head.appendChild(nrules);
13350            ss = nrules.styleSheet;
13351            ss.cssText = cssText;
13352        }else{
13353            try{
13354                 nrules.appendChild(doc.createTextNode(cssText));
13355            }catch(e){
13356                nrules.cssText = cssText; 
13357            }
13358            head.appendChild(nrules);
13359            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13360        }
13361        this.cacheStyleSheet(ss);
13362        return ss;
13363    },
13364
13365    /**
13366     * Removes a style or link tag by id
13367     * @param {String} id The id of the tag
13368     */
13369    removeStyleSheet : function(id){
13370        var existing = doc.getElementById(id);
13371        if(existing){
13372            existing.parentNode.removeChild(existing);
13373        }
13374    },
13375
13376    /**
13377     * Dynamically swaps an existing stylesheet reference for a new one
13378     * @param {String} id The id of an existing link tag to remove
13379     * @param {String} url The href of the new stylesheet to include
13380     */
13381    swapStyleSheet : function(id, url){
13382        this.removeStyleSheet(id);
13383        var ss = doc.createElement("link");
13384        ss.setAttribute("rel", "stylesheet");
13385        ss.setAttribute("type", "text/css");
13386        ss.setAttribute("id", id);
13387        ss.setAttribute("href", url);
13388        doc.getElementsByTagName("head")[0].appendChild(ss);
13389    },
13390    
13391    /**
13392     * Refresh the rule cache if you have dynamically added stylesheets
13393     * @return {Object} An object (hash) of rules indexed by selector
13394     */
13395    refreshCache : function(){
13396        return this.getRules(true);
13397    },
13398
13399    // private
13400    cacheStyleSheet : function(stylesheet){
13401        if(!rules){
13402            rules = {};
13403        }
13404        try{// try catch for cross domain access issue
13405            var ssRules = stylesheet.cssRules || stylesheet.rules;
13406            for(var j = ssRules.length-1; j >= 0; --j){
13407                rules[ssRules[j].selectorText] = ssRules[j];
13408            }
13409        }catch(e){}
13410    },
13411    
13412    /**
13413     * Gets all css rules for the document
13414     * @param {Boolean} refreshCache true to refresh the internal cache
13415     * @return {Object} An object (hash) of rules indexed by selector
13416     */
13417    getRules : function(refreshCache){
13418                 if(rules == null || refreshCache){
13419                         rules = {};
13420                         var ds = doc.styleSheets;
13421                         for(var i =0, len = ds.length; i < len; i++){
13422                             try{
13423                         this.cacheStyleSheet(ds[i]);
13424                     }catch(e){} 
13425                 }
13426                 }
13427                 return rules;
13428         },
13429         
13430         /**
13431     * Gets an an individual CSS rule by selector(s)
13432     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13433     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13434     * @return {CSSRule} The CSS rule or null if one is not found
13435     */
13436    getRule : function(selector, refreshCache){
13437                 var rs = this.getRules(refreshCache);
13438                 if(!(selector instanceof Array)){
13439                     return rs[selector];
13440                 }
13441                 for(var i = 0; i < selector.length; i++){
13442                         if(rs[selector[i]]){
13443                                 return rs[selector[i]];
13444                         }
13445                 }
13446                 return null;
13447         },
13448         
13449         
13450         /**
13451     * Updates a rule property
13452     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13453     * @param {String} property The css property
13454     * @param {String} value The new value for the property
13455     * @return {Boolean} true If a rule was found and updated
13456     */
13457    updateRule : function(selector, property, value){
13458                 if(!(selector instanceof Array)){
13459                         var rule = this.getRule(selector);
13460                         if(rule){
13461                                 rule.style[property.replace(camelRe, camelFn)] = value;
13462                                 return true;
13463                         }
13464                 }else{
13465                         for(var i = 0; i < selector.length; i++){
13466                                 if(this.updateRule(selector[i], property, value)){
13467                                         return true;
13468                                 }
13469                         }
13470                 }
13471                 return false;
13472         }
13473    };   
13474 }();/*
13475  * Based on:
13476  * Ext JS Library 1.1.1
13477  * Copyright(c) 2006-2007, Ext JS, LLC.
13478  *
13479  * Originally Released Under LGPL - original licence link has changed is not relivant.
13480  *
13481  * Fork - LGPL
13482  * <script type="text/javascript">
13483  */
13484
13485  
13486
13487 /**
13488  * @class Roo.util.ClickRepeater
13489  * @extends Roo.util.Observable
13490  * 
13491  * A wrapper class which can be applied to any element. Fires a "click" event while the
13492  * mouse is pressed. The interval between firings may be specified in the config but
13493  * defaults to 10 milliseconds.
13494  * 
13495  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13496  * 
13497  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13498  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13499  * Similar to an autorepeat key delay.
13500  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13501  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13502  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13503  *           "interval" and "delay" are ignored. "immediate" is honored.
13504  * @cfg {Boolean} preventDefault True to prevent the default click event
13505  * @cfg {Boolean} stopDefault True to stop the default click event
13506  * 
13507  * @history
13508  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13509  *     2007-02-02 jvs Renamed to ClickRepeater
13510  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13511  *
13512  *  @constructor
13513  * @param {String/HTMLElement/Element} el The element to listen on
13514  * @param {Object} config
13515  **/
13516 Roo.util.ClickRepeater = function(el, config)
13517 {
13518     this.el = Roo.get(el);
13519     this.el.unselectable();
13520
13521     Roo.apply(this, config);
13522
13523     this.addEvents({
13524     /**
13525      * @event mousedown
13526      * Fires when the mouse button is depressed.
13527      * @param {Roo.util.ClickRepeater} this
13528      */
13529         "mousedown" : true,
13530     /**
13531      * @event click
13532      * Fires on a specified interval during the time the element is pressed.
13533      * @param {Roo.util.ClickRepeater} this
13534      */
13535         "click" : true,
13536     /**
13537      * @event mouseup
13538      * Fires when the mouse key is released.
13539      * @param {Roo.util.ClickRepeater} this
13540      */
13541         "mouseup" : true
13542     });
13543
13544     this.el.on("mousedown", this.handleMouseDown, this);
13545     if(this.preventDefault || this.stopDefault){
13546         this.el.on("click", function(e){
13547             if(this.preventDefault){
13548                 e.preventDefault();
13549             }
13550             if(this.stopDefault){
13551                 e.stopEvent();
13552             }
13553         }, this);
13554     }
13555
13556     // allow inline handler
13557     if(this.handler){
13558         this.on("click", this.handler,  this.scope || this);
13559     }
13560
13561     Roo.util.ClickRepeater.superclass.constructor.call(this);
13562 };
13563
13564 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13565     interval : 20,
13566     delay: 250,
13567     preventDefault : true,
13568     stopDefault : false,
13569     timer : 0,
13570
13571     // private
13572     handleMouseDown : function(){
13573         clearTimeout(this.timer);
13574         this.el.blur();
13575         if(this.pressClass){
13576             this.el.addClass(this.pressClass);
13577         }
13578         this.mousedownTime = new Date();
13579
13580         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13581         this.el.on("mouseout", this.handleMouseOut, this);
13582
13583         this.fireEvent("mousedown", this);
13584         this.fireEvent("click", this);
13585         
13586         this.timer = this.click.defer(this.delay || this.interval, this);
13587     },
13588
13589     // private
13590     click : function(){
13591         this.fireEvent("click", this);
13592         this.timer = this.click.defer(this.getInterval(), this);
13593     },
13594
13595     // private
13596     getInterval: function(){
13597         if(!this.accelerate){
13598             return this.interval;
13599         }
13600         var pressTime = this.mousedownTime.getElapsed();
13601         if(pressTime < 500){
13602             return 400;
13603         }else if(pressTime < 1700){
13604             return 320;
13605         }else if(pressTime < 2600){
13606             return 250;
13607         }else if(pressTime < 3500){
13608             return 180;
13609         }else if(pressTime < 4400){
13610             return 140;
13611         }else if(pressTime < 5300){
13612             return 80;
13613         }else if(pressTime < 6200){
13614             return 50;
13615         }else{
13616             return 10;
13617         }
13618     },
13619
13620     // private
13621     handleMouseOut : function(){
13622         clearTimeout(this.timer);
13623         if(this.pressClass){
13624             this.el.removeClass(this.pressClass);
13625         }
13626         this.el.on("mouseover", this.handleMouseReturn, this);
13627     },
13628
13629     // private
13630     handleMouseReturn : function(){
13631         this.el.un("mouseover", this.handleMouseReturn);
13632         if(this.pressClass){
13633             this.el.addClass(this.pressClass);
13634         }
13635         this.click();
13636     },
13637
13638     // private
13639     handleMouseUp : function(){
13640         clearTimeout(this.timer);
13641         this.el.un("mouseover", this.handleMouseReturn);
13642         this.el.un("mouseout", this.handleMouseOut);
13643         Roo.get(document).un("mouseup", this.handleMouseUp);
13644         this.el.removeClass(this.pressClass);
13645         this.fireEvent("mouseup", this);
13646     }
13647 });/*
13648  * Based on:
13649  * Ext JS Library 1.1.1
13650  * Copyright(c) 2006-2007, Ext JS, LLC.
13651  *
13652  * Originally Released Under LGPL - original licence link has changed is not relivant.
13653  *
13654  * Fork - LGPL
13655  * <script type="text/javascript">
13656  */
13657
13658  
13659 /**
13660  * @class Roo.KeyNav
13661  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13662  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13663  * way to implement custom navigation schemes for any UI component.</p>
13664  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13665  * pageUp, pageDown, del, home, end.  Usage:</p>
13666  <pre><code>
13667 var nav = new Roo.KeyNav("my-element", {
13668     "left" : function(e){
13669         this.moveLeft(e.ctrlKey);
13670     },
13671     "right" : function(e){
13672         this.moveRight(e.ctrlKey);
13673     },
13674     "enter" : function(e){
13675         this.save();
13676     },
13677     scope : this
13678 });
13679 </code></pre>
13680  * @constructor
13681  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13682  * @param {Object} config The config
13683  */
13684 Roo.KeyNav = function(el, config){
13685     this.el = Roo.get(el);
13686     Roo.apply(this, config);
13687     if(!this.disabled){
13688         this.disabled = true;
13689         this.enable();
13690     }
13691 };
13692
13693 Roo.KeyNav.prototype = {
13694     /**
13695      * @cfg {Boolean} disabled
13696      * True to disable this KeyNav instance (defaults to false)
13697      */
13698     disabled : false,
13699     /**
13700      * @cfg {String} defaultEventAction
13701      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13702      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13703      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13704      */
13705     defaultEventAction: "stopEvent",
13706     /**
13707      * @cfg {Boolean} forceKeyDown
13708      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13709      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13710      * handle keydown instead of keypress.
13711      */
13712     forceKeyDown : false,
13713
13714     // private
13715     prepareEvent : function(e){
13716         var k = e.getKey();
13717         var h = this.keyToHandler[k];
13718         //if(h && this[h]){
13719         //    e.stopPropagation();
13720         //}
13721         if(Roo.isSafari && h && k >= 37 && k <= 40){
13722             e.stopEvent();
13723         }
13724     },
13725
13726     // private
13727     relay : function(e){
13728         var k = e.getKey();
13729         var h = this.keyToHandler[k];
13730         if(h && this[h]){
13731             if(this.doRelay(e, this[h], h) !== true){
13732                 e[this.defaultEventAction]();
13733             }
13734         }
13735     },
13736
13737     // private
13738     doRelay : function(e, h, hname){
13739         return h.call(this.scope || this, e);
13740     },
13741
13742     // possible handlers
13743     enter : false,
13744     left : false,
13745     right : false,
13746     up : false,
13747     down : false,
13748     tab : false,
13749     esc : false,
13750     pageUp : false,
13751     pageDown : false,
13752     del : false,
13753     home : false,
13754     end : false,
13755
13756     // quick lookup hash
13757     keyToHandler : {
13758         37 : "left",
13759         39 : "right",
13760         38 : "up",
13761         40 : "down",
13762         33 : "pageUp",
13763         34 : "pageDown",
13764         46 : "del",
13765         36 : "home",
13766         35 : "end",
13767         13 : "enter",
13768         27 : "esc",
13769         9  : "tab"
13770     },
13771
13772         /**
13773          * Enable this KeyNav
13774          */
13775         enable: function(){
13776                 if(this.disabled){
13777             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13778             // the EventObject will normalize Safari automatically
13779             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13780                 this.el.on("keydown", this.relay,  this);
13781             }else{
13782                 this.el.on("keydown", this.prepareEvent,  this);
13783                 this.el.on("keypress", this.relay,  this);
13784             }
13785                     this.disabled = false;
13786                 }
13787         },
13788
13789         /**
13790          * Disable this KeyNav
13791          */
13792         disable: function(){
13793                 if(!this.disabled){
13794                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13795                 this.el.un("keydown", this.relay);
13796             }else{
13797                 this.el.un("keydown", this.prepareEvent);
13798                 this.el.un("keypress", this.relay);
13799             }
13800                     this.disabled = true;
13801                 }
13802         }
13803 };/*
13804  * Based on:
13805  * Ext JS Library 1.1.1
13806  * Copyright(c) 2006-2007, Ext JS, LLC.
13807  *
13808  * Originally Released Under LGPL - original licence link has changed is not relivant.
13809  *
13810  * Fork - LGPL
13811  * <script type="text/javascript">
13812  */
13813
13814  
13815 /**
13816  * @class Roo.KeyMap
13817  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13818  * The constructor accepts the same config object as defined by {@link #addBinding}.
13819  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13820  * combination it will call the function with this signature (if the match is a multi-key
13821  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13822  * A KeyMap can also handle a string representation of keys.<br />
13823  * Usage:
13824  <pre><code>
13825 // map one key by key code
13826 var map = new Roo.KeyMap("my-element", {
13827     key: 13, // or Roo.EventObject.ENTER
13828     fn: myHandler,
13829     scope: myObject
13830 });
13831
13832 // map multiple keys to one action by string
13833 var map = new Roo.KeyMap("my-element", {
13834     key: "a\r\n\t",
13835     fn: myHandler,
13836     scope: myObject
13837 });
13838
13839 // map multiple keys to multiple actions by strings and array of codes
13840 var map = new Roo.KeyMap("my-element", [
13841     {
13842         key: [10,13],
13843         fn: function(){ alert("Return was pressed"); }
13844     }, {
13845         key: "abc",
13846         fn: function(){ alert('a, b or c was pressed'); }
13847     }, {
13848         key: "\t",
13849         ctrl:true,
13850         shift:true,
13851         fn: function(){ alert('Control + shift + tab was pressed.'); }
13852     }
13853 ]);
13854 </code></pre>
13855  * <b>Note: A KeyMap starts enabled</b>
13856  * @constructor
13857  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13858  * @param {Object} config The config (see {@link #addBinding})
13859  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13860  */
13861 Roo.KeyMap = function(el, config, eventName){
13862     this.el  = Roo.get(el);
13863     this.eventName = eventName || "keydown";
13864     this.bindings = [];
13865     if(config){
13866         this.addBinding(config);
13867     }
13868     this.enable();
13869 };
13870
13871 Roo.KeyMap.prototype = {
13872     /**
13873      * True to stop the event from bubbling and prevent the default browser action if the
13874      * key was handled by the KeyMap (defaults to false)
13875      * @type Boolean
13876      */
13877     stopEvent : false,
13878
13879     /**
13880      * Add a new binding to this KeyMap. The following config object properties are supported:
13881      * <pre>
13882 Property    Type             Description
13883 ----------  ---------------  ----------------------------------------------------------------------
13884 key         String/Array     A single keycode or an array of keycodes to handle
13885 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13886 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13887 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13888 fn          Function         The function to call when KeyMap finds the expected key combination
13889 scope       Object           The scope of the callback function
13890 </pre>
13891      *
13892      * Usage:
13893      * <pre><code>
13894 // Create a KeyMap
13895 var map = new Roo.KeyMap(document, {
13896     key: Roo.EventObject.ENTER,
13897     fn: handleKey,
13898     scope: this
13899 });
13900
13901 //Add a new binding to the existing KeyMap later
13902 map.addBinding({
13903     key: 'abc',
13904     shift: true,
13905     fn: handleKey,
13906     scope: this
13907 });
13908 </code></pre>
13909      * @param {Object/Array} config A single KeyMap config or an array of configs
13910      */
13911         addBinding : function(config){
13912         if(config instanceof Array){
13913             for(var i = 0, len = config.length; i < len; i++){
13914                 this.addBinding(config[i]);
13915             }
13916             return;
13917         }
13918         var keyCode = config.key,
13919             shift = config.shift, 
13920             ctrl = config.ctrl, 
13921             alt = config.alt,
13922             fn = config.fn,
13923             scope = config.scope;
13924         if(typeof keyCode == "string"){
13925             var ks = [];
13926             var keyString = keyCode.toUpperCase();
13927             for(var j = 0, len = keyString.length; j < len; j++){
13928                 ks.push(keyString.charCodeAt(j));
13929             }
13930             keyCode = ks;
13931         }
13932         var keyArray = keyCode instanceof Array;
13933         var handler = function(e){
13934             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13935                 var k = e.getKey();
13936                 if(keyArray){
13937                     for(var i = 0, len = keyCode.length; i < len; i++){
13938                         if(keyCode[i] == k){
13939                           if(this.stopEvent){
13940                               e.stopEvent();
13941                           }
13942                           fn.call(scope || window, k, e);
13943                           return;
13944                         }
13945                     }
13946                 }else{
13947                     if(k == keyCode){
13948                         if(this.stopEvent){
13949                            e.stopEvent();
13950                         }
13951                         fn.call(scope || window, k, e);
13952                     }
13953                 }
13954             }
13955         };
13956         this.bindings.push(handler);  
13957         },
13958
13959     /**
13960      * Shorthand for adding a single key listener
13961      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13962      * following options:
13963      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13964      * @param {Function} fn The function to call
13965      * @param {Object} scope (optional) The scope of the function
13966      */
13967     on : function(key, fn, scope){
13968         var keyCode, shift, ctrl, alt;
13969         if(typeof key == "object" && !(key instanceof Array)){
13970             keyCode = key.key;
13971             shift = key.shift;
13972             ctrl = key.ctrl;
13973             alt = key.alt;
13974         }else{
13975             keyCode = key;
13976         }
13977         this.addBinding({
13978             key: keyCode,
13979             shift: shift,
13980             ctrl: ctrl,
13981             alt: alt,
13982             fn: fn,
13983             scope: scope
13984         })
13985     },
13986
13987     // private
13988     handleKeyDown : function(e){
13989             if(this.enabled){ //just in case
13990             var b = this.bindings;
13991             for(var i = 0, len = b.length; i < len; i++){
13992                 b[i].call(this, e);
13993             }
13994             }
13995         },
13996         
13997         /**
13998          * Returns true if this KeyMap is enabled
13999          * @return {Boolean} 
14000          */
14001         isEnabled : function(){
14002             return this.enabled;  
14003         },
14004         
14005         /**
14006          * Enables this KeyMap
14007          */
14008         enable: function(){
14009                 if(!this.enabled){
14010                     this.el.on(this.eventName, this.handleKeyDown, this);
14011                     this.enabled = true;
14012                 }
14013         },
14014
14015         /**
14016          * Disable this KeyMap
14017          */
14018         disable: function(){
14019                 if(this.enabled){
14020                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14021                     this.enabled = false;
14022                 }
14023         }
14024 };/*
14025  * Based on:
14026  * Ext JS Library 1.1.1
14027  * Copyright(c) 2006-2007, Ext JS, LLC.
14028  *
14029  * Originally Released Under LGPL - original licence link has changed is not relivant.
14030  *
14031  * Fork - LGPL
14032  * <script type="text/javascript">
14033  */
14034
14035  
14036 /**
14037  * @class Roo.util.TextMetrics
14038  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14039  * wide, in pixels, a given block of text will be.
14040  * @singleton
14041  */
14042 Roo.util.TextMetrics = function(){
14043     var shared;
14044     return {
14045         /**
14046          * Measures the size of the specified text
14047          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14048          * that can affect the size of the rendered text
14049          * @param {String} text The text to measure
14050          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14051          * in order to accurately measure the text height
14052          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14053          */
14054         measure : function(el, text, fixedWidth){
14055             if(!shared){
14056                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14057             }
14058             shared.bind(el);
14059             shared.setFixedWidth(fixedWidth || 'auto');
14060             return shared.getSize(text);
14061         },
14062
14063         /**
14064          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14065          * the overhead of multiple calls to initialize the style properties on each measurement.
14066          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14067          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14068          * in order to accurately measure the text height
14069          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14070          */
14071         createInstance : function(el, fixedWidth){
14072             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14073         }
14074     };
14075 }();
14076
14077  
14078
14079 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14080     var ml = new Roo.Element(document.createElement('div'));
14081     document.body.appendChild(ml.dom);
14082     ml.position('absolute');
14083     ml.setLeftTop(-1000, -1000);
14084     ml.hide();
14085
14086     if(fixedWidth){
14087         ml.setWidth(fixedWidth);
14088     }
14089      
14090     var instance = {
14091         /**
14092          * Returns the size of the specified text based on the internal element's style and width properties
14093          * @memberOf Roo.util.TextMetrics.Instance#
14094          * @param {String} text The text to measure
14095          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14096          */
14097         getSize : function(text){
14098             ml.update(text);
14099             var s = ml.getSize();
14100             ml.update('');
14101             return s;
14102         },
14103
14104         /**
14105          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14106          * that can affect the size of the rendered text
14107          * @memberOf Roo.util.TextMetrics.Instance#
14108          * @param {String/HTMLElement} el The element, dom node or id
14109          */
14110         bind : function(el){
14111             ml.setStyle(
14112                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14113             );
14114         },
14115
14116         /**
14117          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14118          * to set a fixed width in order to accurately measure the text height.
14119          * @memberOf Roo.util.TextMetrics.Instance#
14120          * @param {Number} width The width to set on the element
14121          */
14122         setFixedWidth : function(width){
14123             ml.setWidth(width);
14124         },
14125
14126         /**
14127          * Returns the measured width of the specified text
14128          * @memberOf Roo.util.TextMetrics.Instance#
14129          * @param {String} text The text to measure
14130          * @return {Number} width The width in pixels
14131          */
14132         getWidth : function(text){
14133             ml.dom.style.width = 'auto';
14134             return this.getSize(text).width;
14135         },
14136
14137         /**
14138          * Returns the measured height of the specified text.  For multiline text, be sure to call
14139          * {@link #setFixedWidth} if necessary.
14140          * @memberOf Roo.util.TextMetrics.Instance#
14141          * @param {String} text The text to measure
14142          * @return {Number} height The height in pixels
14143          */
14144         getHeight : function(text){
14145             return this.getSize(text).height;
14146         }
14147     };
14148
14149     instance.bind(bindTo);
14150
14151     return instance;
14152 };
14153
14154 // backwards compat
14155 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14156  * Based on:
14157  * Ext JS Library 1.1.1
14158  * Copyright(c) 2006-2007, Ext JS, LLC.
14159  *
14160  * Originally Released Under LGPL - original licence link has changed is not relivant.
14161  *
14162  * Fork - LGPL
14163  * <script type="text/javascript">
14164  */
14165
14166 /**
14167  * @class Roo.state.Provider
14168  * Abstract base class for state provider implementations. This class provides methods
14169  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14170  * Provider interface.
14171  */
14172 Roo.state.Provider = function(){
14173     /**
14174      * @event statechange
14175      * Fires when a state change occurs.
14176      * @param {Provider} this This state provider
14177      * @param {String} key The state key which was changed
14178      * @param {String} value The encoded value for the state
14179      */
14180     this.addEvents({
14181         "statechange": true
14182     });
14183     this.state = {};
14184     Roo.state.Provider.superclass.constructor.call(this);
14185 };
14186 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14187     /**
14188      * Returns the current value for a key
14189      * @param {String} name The key name
14190      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14191      * @return {Mixed} The state data
14192      */
14193     get : function(name, defaultValue){
14194         return typeof this.state[name] == "undefined" ?
14195             defaultValue : this.state[name];
14196     },
14197     
14198     /**
14199      * Clears a value from the state
14200      * @param {String} name The key name
14201      */
14202     clear : function(name){
14203         delete this.state[name];
14204         this.fireEvent("statechange", this, name, null);
14205     },
14206     
14207     /**
14208      * Sets the value for a key
14209      * @param {String} name The key name
14210      * @param {Mixed} value The value to set
14211      */
14212     set : function(name, value){
14213         this.state[name] = value;
14214         this.fireEvent("statechange", this, name, value);
14215     },
14216     
14217     /**
14218      * Decodes a string previously encoded with {@link #encodeValue}.
14219      * @param {String} value The value to decode
14220      * @return {Mixed} The decoded value
14221      */
14222     decodeValue : function(cookie){
14223         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14224         var matches = re.exec(unescape(cookie));
14225         if(!matches || !matches[1]) return; // non state cookie
14226         var type = matches[1];
14227         var v = matches[2];
14228         switch(type){
14229             case "n":
14230                 return parseFloat(v);
14231             case "d":
14232                 return new Date(Date.parse(v));
14233             case "b":
14234                 return (v == "1");
14235             case "a":
14236                 var all = [];
14237                 var values = v.split("^");
14238                 for(var i = 0, len = values.length; i < len; i++){
14239                     all.push(this.decodeValue(values[i]));
14240                 }
14241                 return all;
14242            case "o":
14243                 var all = {};
14244                 var values = v.split("^");
14245                 for(var i = 0, len = values.length; i < len; i++){
14246                     var kv = values[i].split("=");
14247                     all[kv[0]] = this.decodeValue(kv[1]);
14248                 }
14249                 return all;
14250            default:
14251                 return v;
14252         }
14253     },
14254     
14255     /**
14256      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14257      * @param {Mixed} value The value to encode
14258      * @return {String} The encoded value
14259      */
14260     encodeValue : function(v){
14261         var enc;
14262         if(typeof v == "number"){
14263             enc = "n:" + v;
14264         }else if(typeof v == "boolean"){
14265             enc = "b:" + (v ? "1" : "0");
14266         }else if(v instanceof Date){
14267             enc = "d:" + v.toGMTString();
14268         }else if(v instanceof Array){
14269             var flat = "";
14270             for(var i = 0, len = v.length; i < len; i++){
14271                 flat += this.encodeValue(v[i]);
14272                 if(i != len-1) flat += "^";
14273             }
14274             enc = "a:" + flat;
14275         }else if(typeof v == "object"){
14276             var flat = "";
14277             for(var key in v){
14278                 if(typeof v[key] != "function"){
14279                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14280                 }
14281             }
14282             enc = "o:" + flat.substring(0, flat.length-1);
14283         }else{
14284             enc = "s:" + v;
14285         }
14286         return escape(enc);        
14287     }
14288 });
14289
14290 /*
14291  * Based on:
14292  * Ext JS Library 1.1.1
14293  * Copyright(c) 2006-2007, Ext JS, LLC.
14294  *
14295  * Originally Released Under LGPL - original licence link has changed is not relivant.
14296  *
14297  * Fork - LGPL
14298  * <script type="text/javascript">
14299  */
14300 /**
14301  * @class Roo.state.Manager
14302  * This is the global state manager. By default all components that are "state aware" check this class
14303  * for state information if you don't pass them a custom state provider. In order for this class
14304  * to be useful, it must be initialized with a provider when your application initializes.
14305  <pre><code>
14306 // in your initialization function
14307 init : function(){
14308    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14309    ...
14310    // supposed you have a {@link Roo.BorderLayout}
14311    var layout = new Roo.BorderLayout(...);
14312    layout.restoreState();
14313    // or a {Roo.BasicDialog}
14314    var dialog = new Roo.BasicDialog(...);
14315    dialog.restoreState();
14316  </code></pre>
14317  * @singleton
14318  */
14319 Roo.state.Manager = function(){
14320     var provider = new Roo.state.Provider();
14321     
14322     return {
14323         /**
14324          * Configures the default state provider for your application
14325          * @param {Provider} stateProvider The state provider to set
14326          */
14327         setProvider : function(stateProvider){
14328             provider = stateProvider;
14329         },
14330         
14331         /**
14332          * Returns the current value for a key
14333          * @param {String} name The key name
14334          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14335          * @return {Mixed} The state data
14336          */
14337         get : function(key, defaultValue){
14338             return provider.get(key, defaultValue);
14339         },
14340         
14341         /**
14342          * Sets the value for a key
14343          * @param {String} name The key name
14344          * @param {Mixed} value The state data
14345          */
14346          set : function(key, value){
14347             provider.set(key, value);
14348         },
14349         
14350         /**
14351          * Clears a value from the state
14352          * @param {String} name The key name
14353          */
14354         clear : function(key){
14355             provider.clear(key);
14356         },
14357         
14358         /**
14359          * Gets the currently configured state provider
14360          * @return {Provider} The state provider
14361          */
14362         getProvider : function(){
14363             return provider;
14364         }
14365     };
14366 }();
14367 /*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377 /**
14378  * @class Roo.state.CookieProvider
14379  * @extends Roo.state.Provider
14380  * The default Provider implementation which saves state via cookies.
14381  * <br />Usage:
14382  <pre><code>
14383    var cp = new Roo.state.CookieProvider({
14384        path: "/cgi-bin/",
14385        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14386        domain: "roojs.com"
14387    })
14388    Roo.state.Manager.setProvider(cp);
14389  </code></pre>
14390  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14391  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14392  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14393  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14394  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14395  * domain the page is running on including the 'www' like 'www.roojs.com')
14396  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14397  * @constructor
14398  * Create a new CookieProvider
14399  * @param {Object} config The configuration object
14400  */
14401 Roo.state.CookieProvider = function(config){
14402     Roo.state.CookieProvider.superclass.constructor.call(this);
14403     this.path = "/";
14404     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14405     this.domain = null;
14406     this.secure = false;
14407     Roo.apply(this, config);
14408     this.state = this.readCookies();
14409 };
14410
14411 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14412     // private
14413     set : function(name, value){
14414         if(typeof value == "undefined" || value === null){
14415             this.clear(name);
14416             return;
14417         }
14418         this.setCookie(name, value);
14419         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14420     },
14421
14422     // private
14423     clear : function(name){
14424         this.clearCookie(name);
14425         Roo.state.CookieProvider.superclass.clear.call(this, name);
14426     },
14427
14428     // private
14429     readCookies : function(){
14430         var cookies = {};
14431         var c = document.cookie + ";";
14432         var re = /\s?(.*?)=(.*?);/g;
14433         var matches;
14434         while((matches = re.exec(c)) != null){
14435             var name = matches[1];
14436             var value = matches[2];
14437             if(name && name.substring(0,3) == "ys-"){
14438                 cookies[name.substr(3)] = this.decodeValue(value);
14439             }
14440         }
14441         return cookies;
14442     },
14443
14444     // private
14445     setCookie : function(name, value){
14446         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14447            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14448            ((this.path == null) ? "" : ("; path=" + this.path)) +
14449            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14450            ((this.secure == true) ? "; secure" : "");
14451     },
14452
14453     // private
14454     clearCookie : function(name){
14455         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14456            ((this.path == null) ? "" : ("; path=" + this.path)) +
14457            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14458            ((this.secure == true) ? "; secure" : "");
14459     }
14460 });/*
14461  * Based on:
14462  * Ext JS Library 1.1.1
14463  * Copyright(c) 2006-2007, Ext JS, LLC.
14464  *
14465  * Originally Released Under LGPL - original licence link has changed is not relivant.
14466  *
14467  * Fork - LGPL
14468  * <script type="text/javascript">
14469  */
14470
14471
14472
14473 /*
14474  * These classes are derivatives of the similarly named classes in the YUI Library.
14475  * The original license:
14476  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14477  * Code licensed under the BSD License:
14478  * http://developer.yahoo.net/yui/license.txt
14479  */
14480
14481 (function() {
14482
14483 var Event=Roo.EventManager;
14484 var Dom=Roo.lib.Dom;
14485
14486 /**
14487  * @class Roo.dd.DragDrop
14488  * @extends Roo.util.Observable
14489  * Defines the interface and base operation of items that that can be
14490  * dragged or can be drop targets.  It was designed to be extended, overriding
14491  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14492  * Up to three html elements can be associated with a DragDrop instance:
14493  * <ul>
14494  * <li>linked element: the element that is passed into the constructor.
14495  * This is the element which defines the boundaries for interaction with
14496  * other DragDrop objects.</li>
14497  * <li>handle element(s): The drag operation only occurs if the element that
14498  * was clicked matches a handle element.  By default this is the linked
14499  * element, but there are times that you will want only a portion of the
14500  * linked element to initiate the drag operation, and the setHandleElId()
14501  * method provides a way to define this.</li>
14502  * <li>drag element: this represents the element that would be moved along
14503  * with the cursor during a drag operation.  By default, this is the linked
14504  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14505  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14506  * </li>
14507  * </ul>
14508  * This class should not be instantiated until the onload event to ensure that
14509  * the associated elements are available.
14510  * The following would define a DragDrop obj that would interact with any
14511  * other DragDrop obj in the "group1" group:
14512  * <pre>
14513  *  dd = new Roo.dd.DragDrop("div1", "group1");
14514  * </pre>
14515  * Since none of the event handlers have been implemented, nothing would
14516  * actually happen if you were to run the code above.  Normally you would
14517  * override this class or one of the default implementations, but you can
14518  * also override the methods you want on an instance of the class...
14519  * <pre>
14520  *  dd.onDragDrop = function(e, id) {
14521  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14522  *  }
14523  * </pre>
14524  * @constructor
14525  * @param {String} id of the element that is linked to this instance
14526  * @param {String} sGroup the group of related DragDrop objects
14527  * @param {object} config an object containing configurable attributes
14528  *                Valid properties for DragDrop:
14529  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14530  */
14531 Roo.dd.DragDrop = function(id, sGroup, config) {
14532     if (id) {
14533         this.init(id, sGroup, config);
14534     }
14535     
14536 };
14537
14538 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14539
14540     /**
14541      * The id of the element associated with this object.  This is what we
14542      * refer to as the "linked element" because the size and position of
14543      * this element is used to determine when the drag and drop objects have
14544      * interacted.
14545      * @property id
14546      * @type String
14547      */
14548     id: null,
14549
14550     /**
14551      * Configuration attributes passed into the constructor
14552      * @property config
14553      * @type object
14554      */
14555     config: null,
14556
14557     /**
14558      * The id of the element that will be dragged.  By default this is same
14559      * as the linked element , but could be changed to another element. Ex:
14560      * Roo.dd.DDProxy
14561      * @property dragElId
14562      * @type String
14563      * @private
14564      */
14565     dragElId: null,
14566
14567     /**
14568      * the id of the element that initiates the drag operation.  By default
14569      * this is the linked element, but could be changed to be a child of this
14570      * element.  This lets us do things like only starting the drag when the
14571      * header element within the linked html element is clicked.
14572      * @property handleElId
14573      * @type String
14574      * @private
14575      */
14576     handleElId: null,
14577
14578     /**
14579      * An associative array of HTML tags that will be ignored if clicked.
14580      * @property invalidHandleTypes
14581      * @type {string: string}
14582      */
14583     invalidHandleTypes: null,
14584
14585     /**
14586      * An associative array of ids for elements that will be ignored if clicked
14587      * @property invalidHandleIds
14588      * @type {string: string}
14589      */
14590     invalidHandleIds: null,
14591
14592     /**
14593      * An indexted array of css class names for elements that will be ignored
14594      * if clicked.
14595      * @property invalidHandleClasses
14596      * @type string[]
14597      */
14598     invalidHandleClasses: null,
14599
14600     /**
14601      * The linked element's absolute X position at the time the drag was
14602      * started
14603      * @property startPageX
14604      * @type int
14605      * @private
14606      */
14607     startPageX: 0,
14608
14609     /**
14610      * The linked element's absolute X position at the time the drag was
14611      * started
14612      * @property startPageY
14613      * @type int
14614      * @private
14615      */
14616     startPageY: 0,
14617
14618     /**
14619      * The group defines a logical collection of DragDrop objects that are
14620      * related.  Instances only get events when interacting with other
14621      * DragDrop object in the same group.  This lets us define multiple
14622      * groups using a single DragDrop subclass if we want.
14623      * @property groups
14624      * @type {string: string}
14625      */
14626     groups: null,
14627
14628     /**
14629      * Individual drag/drop instances can be locked.  This will prevent
14630      * onmousedown start drag.
14631      * @property locked
14632      * @type boolean
14633      * @private
14634      */
14635     locked: false,
14636
14637     /**
14638      * Lock this instance
14639      * @method lock
14640      */
14641     lock: function() { this.locked = true; },
14642
14643     /**
14644      * Unlock this instace
14645      * @method unlock
14646      */
14647     unlock: function() { this.locked = false; },
14648
14649     /**
14650      * By default, all insances can be a drop target.  This can be disabled by
14651      * setting isTarget to false.
14652      * @method isTarget
14653      * @type boolean
14654      */
14655     isTarget: true,
14656
14657     /**
14658      * The padding configured for this drag and drop object for calculating
14659      * the drop zone intersection with this object.
14660      * @method padding
14661      * @type int[]
14662      */
14663     padding: null,
14664
14665     /**
14666      * Cached reference to the linked element
14667      * @property _domRef
14668      * @private
14669      */
14670     _domRef: null,
14671
14672     /**
14673      * Internal typeof flag
14674      * @property __ygDragDrop
14675      * @private
14676      */
14677     __ygDragDrop: true,
14678
14679     /**
14680      * Set to true when horizontal contraints are applied
14681      * @property constrainX
14682      * @type boolean
14683      * @private
14684      */
14685     constrainX: false,
14686
14687     /**
14688      * Set to true when vertical contraints are applied
14689      * @property constrainY
14690      * @type boolean
14691      * @private
14692      */
14693     constrainY: false,
14694
14695     /**
14696      * The left constraint
14697      * @property minX
14698      * @type int
14699      * @private
14700      */
14701     minX: 0,
14702
14703     /**
14704      * The right constraint
14705      * @property maxX
14706      * @type int
14707      * @private
14708      */
14709     maxX: 0,
14710
14711     /**
14712      * The up constraint
14713      * @property minY
14714      * @type int
14715      * @type int
14716      * @private
14717      */
14718     minY: 0,
14719
14720     /**
14721      * The down constraint
14722      * @property maxY
14723      * @type int
14724      * @private
14725      */
14726     maxY: 0,
14727
14728     /**
14729      * Maintain offsets when we resetconstraints.  Set to true when you want
14730      * the position of the element relative to its parent to stay the same
14731      * when the page changes
14732      *
14733      * @property maintainOffset
14734      * @type boolean
14735      */
14736     maintainOffset: false,
14737
14738     /**
14739      * Array of pixel locations the element will snap to if we specified a
14740      * horizontal graduation/interval.  This array is generated automatically
14741      * when you define a tick interval.
14742      * @property xTicks
14743      * @type int[]
14744      */
14745     xTicks: null,
14746
14747     /**
14748      * Array of pixel locations the element will snap to if we specified a
14749      * vertical graduation/interval.  This array is generated automatically
14750      * when you define a tick interval.
14751      * @property yTicks
14752      * @type int[]
14753      */
14754     yTicks: null,
14755
14756     /**
14757      * By default the drag and drop instance will only respond to the primary
14758      * button click (left button for a right-handed mouse).  Set to true to
14759      * allow drag and drop to start with any mouse click that is propogated
14760      * by the browser
14761      * @property primaryButtonOnly
14762      * @type boolean
14763      */
14764     primaryButtonOnly: true,
14765
14766     /**
14767      * The availabe property is false until the linked dom element is accessible.
14768      * @property available
14769      * @type boolean
14770      */
14771     available: false,
14772
14773     /**
14774      * By default, drags can only be initiated if the mousedown occurs in the
14775      * region the linked element is.  This is done in part to work around a
14776      * bug in some browsers that mis-report the mousedown if the previous
14777      * mouseup happened outside of the window.  This property is set to true
14778      * if outer handles are defined.
14779      *
14780      * @property hasOuterHandles
14781      * @type boolean
14782      * @default false
14783      */
14784     hasOuterHandles: false,
14785
14786     /**
14787      * Code that executes immediately before the startDrag event
14788      * @method b4StartDrag
14789      * @private
14790      */
14791     b4StartDrag: function(x, y) { },
14792
14793     /**
14794      * Abstract method called after a drag/drop object is clicked
14795      * and the drag or mousedown time thresholds have beeen met.
14796      * @method startDrag
14797      * @param {int} X click location
14798      * @param {int} Y click location
14799      */
14800     startDrag: function(x, y) { /* override this */ },
14801
14802     /**
14803      * Code that executes immediately before the onDrag event
14804      * @method b4Drag
14805      * @private
14806      */
14807     b4Drag: function(e) { },
14808
14809     /**
14810      * Abstract method called during the onMouseMove event while dragging an
14811      * object.
14812      * @method onDrag
14813      * @param {Event} e the mousemove event
14814      */
14815     onDrag: function(e) { /* override this */ },
14816
14817     /**
14818      * Abstract method called when this element fist begins hovering over
14819      * another DragDrop obj
14820      * @method onDragEnter
14821      * @param {Event} e the mousemove event
14822      * @param {String|DragDrop[]} id In POINT mode, the element
14823      * id this is hovering over.  In INTERSECT mode, an array of one or more
14824      * dragdrop items being hovered over.
14825      */
14826     onDragEnter: function(e, id) { /* override this */ },
14827
14828     /**
14829      * Code that executes immediately before the onDragOver event
14830      * @method b4DragOver
14831      * @private
14832      */
14833     b4DragOver: function(e) { },
14834
14835     /**
14836      * Abstract method called when this element is hovering over another
14837      * DragDrop obj
14838      * @method onDragOver
14839      * @param {Event} e the mousemove event
14840      * @param {String|DragDrop[]} id In POINT mode, the element
14841      * id this is hovering over.  In INTERSECT mode, an array of dd items
14842      * being hovered over.
14843      */
14844     onDragOver: function(e, id) { /* override this */ },
14845
14846     /**
14847      * Code that executes immediately before the onDragOut event
14848      * @method b4DragOut
14849      * @private
14850      */
14851     b4DragOut: function(e) { },
14852
14853     /**
14854      * Abstract method called when we are no longer hovering over an element
14855      * @method onDragOut
14856      * @param {Event} e the mousemove event
14857      * @param {String|DragDrop[]} id In POINT mode, the element
14858      * id this was hovering over.  In INTERSECT mode, an array of dd items
14859      * that the mouse is no longer over.
14860      */
14861     onDragOut: function(e, id) { /* override this */ },
14862
14863     /**
14864      * Code that executes immediately before the onDragDrop event
14865      * @method b4DragDrop
14866      * @private
14867      */
14868     b4DragDrop: function(e) { },
14869
14870     /**
14871      * Abstract method called when this item is dropped on another DragDrop
14872      * obj
14873      * @method onDragDrop
14874      * @param {Event} e the mouseup event
14875      * @param {String|DragDrop[]} id In POINT mode, the element
14876      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14877      * was dropped on.
14878      */
14879     onDragDrop: function(e, id) { /* override this */ },
14880
14881     /**
14882      * Abstract method called when this item is dropped on an area with no
14883      * drop target
14884      * @method onInvalidDrop
14885      * @param {Event} e the mouseup event
14886      */
14887     onInvalidDrop: function(e) { /* override this */ },
14888
14889     /**
14890      * Code that executes immediately before the endDrag event
14891      * @method b4EndDrag
14892      * @private
14893      */
14894     b4EndDrag: function(e) { },
14895
14896     /**
14897      * Fired when we are done dragging the object
14898      * @method endDrag
14899      * @param {Event} e the mouseup event
14900      */
14901     endDrag: function(e) { /* override this */ },
14902
14903     /**
14904      * Code executed immediately before the onMouseDown event
14905      * @method b4MouseDown
14906      * @param {Event} e the mousedown event
14907      * @private
14908      */
14909     b4MouseDown: function(e) {  },
14910
14911     /**
14912      * Event handler that fires when a drag/drop obj gets a mousedown
14913      * @method onMouseDown
14914      * @param {Event} e the mousedown event
14915      */
14916     onMouseDown: function(e) { /* override this */ },
14917
14918     /**
14919      * Event handler that fires when a drag/drop obj gets a mouseup
14920      * @method onMouseUp
14921      * @param {Event} e the mouseup event
14922      */
14923     onMouseUp: function(e) { /* override this */ },
14924
14925     /**
14926      * Override the onAvailable method to do what is needed after the initial
14927      * position was determined.
14928      * @method onAvailable
14929      */
14930     onAvailable: function () {
14931     },
14932
14933     /*
14934      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14935      * @type Object
14936      */
14937     defaultPadding : {left:0, right:0, top:0, bottom:0},
14938
14939     /*
14940      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14941  *
14942  * Usage:
14943  <pre><code>
14944  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14945                 { dragElId: "existingProxyDiv" });
14946  dd.startDrag = function(){
14947      this.constrainTo("parent-id");
14948  };
14949  </code></pre>
14950  * Or you can initalize it using the {@link Roo.Element} object:
14951  <pre><code>
14952  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14953      startDrag : function(){
14954          this.constrainTo("parent-id");
14955      }
14956  });
14957  </code></pre>
14958      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14959      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14960      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14961      * an object containing the sides to pad. For example: {right:10, bottom:10}
14962      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14963      */
14964     constrainTo : function(constrainTo, pad, inContent){
14965         if(typeof pad == "number"){
14966             pad = {left: pad, right:pad, top:pad, bottom:pad};
14967         }
14968         pad = pad || this.defaultPadding;
14969         var b = Roo.get(this.getEl()).getBox();
14970         var ce = Roo.get(constrainTo);
14971         var s = ce.getScroll();
14972         var c, cd = ce.dom;
14973         if(cd == document.body){
14974             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14975         }else{
14976             xy = ce.getXY();
14977             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14978         }
14979
14980
14981         var topSpace = b.y - c.y;
14982         var leftSpace = b.x - c.x;
14983
14984         this.resetConstraints();
14985         this.setXConstraint(leftSpace - (pad.left||0), // left
14986                 c.width - leftSpace - b.width - (pad.right||0) //right
14987         );
14988         this.setYConstraint(topSpace - (pad.top||0), //top
14989                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14990         );
14991     },
14992
14993     /**
14994      * Returns a reference to the linked element
14995      * @method getEl
14996      * @return {HTMLElement} the html element
14997      */
14998     getEl: function() {
14999         if (!this._domRef) {
15000             this._domRef = Roo.getDom(this.id);
15001         }
15002
15003         return this._domRef;
15004     },
15005
15006     /**
15007      * Returns a reference to the actual element to drag.  By default this is
15008      * the same as the html element, but it can be assigned to another
15009      * element. An example of this can be found in Roo.dd.DDProxy
15010      * @method getDragEl
15011      * @return {HTMLElement} the html element
15012      */
15013     getDragEl: function() {
15014         return Roo.getDom(this.dragElId);
15015     },
15016
15017     /**
15018      * Sets up the DragDrop object.  Must be called in the constructor of any
15019      * Roo.dd.DragDrop subclass
15020      * @method init
15021      * @param id the id of the linked element
15022      * @param {String} sGroup the group of related items
15023      * @param {object} config configuration attributes
15024      */
15025     init: function(id, sGroup, config) {
15026         this.initTarget(id, sGroup, config);
15027         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15028         // Event.on(this.id, "selectstart", Event.preventDefault);
15029     },
15030
15031     /**
15032      * Initializes Targeting functionality only... the object does not
15033      * get a mousedown handler.
15034      * @method initTarget
15035      * @param id the id of the linked element
15036      * @param {String} sGroup the group of related items
15037      * @param {object} config configuration attributes
15038      */
15039     initTarget: function(id, sGroup, config) {
15040
15041         // configuration attributes
15042         this.config = config || {};
15043
15044         // create a local reference to the drag and drop manager
15045         this.DDM = Roo.dd.DDM;
15046         // initialize the groups array
15047         this.groups = {};
15048
15049         // assume that we have an element reference instead of an id if the
15050         // parameter is not a string
15051         if (typeof id !== "string") {
15052             id = Roo.id(id);
15053         }
15054
15055         // set the id
15056         this.id = id;
15057
15058         // add to an interaction group
15059         this.addToGroup((sGroup) ? sGroup : "default");
15060
15061         // We don't want to register this as the handle with the manager
15062         // so we just set the id rather than calling the setter.
15063         this.handleElId = id;
15064
15065         // the linked element is the element that gets dragged by default
15066         this.setDragElId(id);
15067
15068         // by default, clicked anchors will not start drag operations.
15069         this.invalidHandleTypes = { A: "A" };
15070         this.invalidHandleIds = {};
15071         this.invalidHandleClasses = [];
15072
15073         this.applyConfig();
15074
15075         this.handleOnAvailable();
15076     },
15077
15078     /**
15079      * Applies the configuration parameters that were passed into the constructor.
15080      * This is supposed to happen at each level through the inheritance chain.  So
15081      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15082      * DragDrop in order to get all of the parameters that are available in
15083      * each object.
15084      * @method applyConfig
15085      */
15086     applyConfig: function() {
15087
15088         // configurable properties:
15089         //    padding, isTarget, maintainOffset, primaryButtonOnly
15090         this.padding           = this.config.padding || [0, 0, 0, 0];
15091         this.isTarget          = (this.config.isTarget !== false);
15092         this.maintainOffset    = (this.config.maintainOffset);
15093         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15094
15095     },
15096
15097     /**
15098      * Executed when the linked element is available
15099      * @method handleOnAvailable
15100      * @private
15101      */
15102     handleOnAvailable: function() {
15103         this.available = true;
15104         this.resetConstraints();
15105         this.onAvailable();
15106     },
15107
15108      /**
15109      * Configures the padding for the target zone in px.  Effectively expands
15110      * (or reduces) the virtual object size for targeting calculations.
15111      * Supports css-style shorthand; if only one parameter is passed, all sides
15112      * will have that padding, and if only two are passed, the top and bottom
15113      * will have the first param, the left and right the second.
15114      * @method setPadding
15115      * @param {int} iTop    Top pad
15116      * @param {int} iRight  Right pad
15117      * @param {int} iBot    Bot pad
15118      * @param {int} iLeft   Left pad
15119      */
15120     setPadding: function(iTop, iRight, iBot, iLeft) {
15121         // this.padding = [iLeft, iRight, iTop, iBot];
15122         if (!iRight && 0 !== iRight) {
15123             this.padding = [iTop, iTop, iTop, iTop];
15124         } else if (!iBot && 0 !== iBot) {
15125             this.padding = [iTop, iRight, iTop, iRight];
15126         } else {
15127             this.padding = [iTop, iRight, iBot, iLeft];
15128         }
15129     },
15130
15131     /**
15132      * Stores the initial placement of the linked element.
15133      * @method setInitialPosition
15134      * @param {int} diffX   the X offset, default 0
15135      * @param {int} diffY   the Y offset, default 0
15136      */
15137     setInitPosition: function(diffX, diffY) {
15138         var el = this.getEl();
15139
15140         if (!this.DDM.verifyEl(el)) {
15141             return;
15142         }
15143
15144         var dx = diffX || 0;
15145         var dy = diffY || 0;
15146
15147         var p = Dom.getXY( el );
15148
15149         this.initPageX = p[0] - dx;
15150         this.initPageY = p[1] - dy;
15151
15152         this.lastPageX = p[0];
15153         this.lastPageY = p[1];
15154
15155
15156         this.setStartPosition(p);
15157     },
15158
15159     /**
15160      * Sets the start position of the element.  This is set when the obj
15161      * is initialized, the reset when a drag is started.
15162      * @method setStartPosition
15163      * @param pos current position (from previous lookup)
15164      * @private
15165      */
15166     setStartPosition: function(pos) {
15167         var p = pos || Dom.getXY( this.getEl() );
15168         this.deltaSetXY = null;
15169
15170         this.startPageX = p[0];
15171         this.startPageY = p[1];
15172     },
15173
15174     /**
15175      * Add this instance to a group of related drag/drop objects.  All
15176      * instances belong to at least one group, and can belong to as many
15177      * groups as needed.
15178      * @method addToGroup
15179      * @param sGroup {string} the name of the group
15180      */
15181     addToGroup: function(sGroup) {
15182         this.groups[sGroup] = true;
15183         this.DDM.regDragDrop(this, sGroup);
15184     },
15185
15186     /**
15187      * Remove's this instance from the supplied interaction group
15188      * @method removeFromGroup
15189      * @param {string}  sGroup  The group to drop
15190      */
15191     removeFromGroup: function(sGroup) {
15192         if (this.groups[sGroup]) {
15193             delete this.groups[sGroup];
15194         }
15195
15196         this.DDM.removeDDFromGroup(this, sGroup);
15197     },
15198
15199     /**
15200      * Allows you to specify that an element other than the linked element
15201      * will be moved with the cursor during a drag
15202      * @method setDragElId
15203      * @param id {string} the id of the element that will be used to initiate the drag
15204      */
15205     setDragElId: function(id) {
15206         this.dragElId = id;
15207     },
15208
15209     /**
15210      * Allows you to specify a child of the linked element that should be
15211      * used to initiate the drag operation.  An example of this would be if
15212      * you have a content div with text and links.  Clicking anywhere in the
15213      * content area would normally start the drag operation.  Use this method
15214      * to specify that an element inside of the content div is the element
15215      * that starts the drag operation.
15216      * @method setHandleElId
15217      * @param id {string} the id of the element that will be used to
15218      * initiate the drag.
15219      */
15220     setHandleElId: function(id) {
15221         if (typeof id !== "string") {
15222             id = Roo.id(id);
15223         }
15224         this.handleElId = id;
15225         this.DDM.regHandle(this.id, id);
15226     },
15227
15228     /**
15229      * Allows you to set an element outside of the linked element as a drag
15230      * handle
15231      * @method setOuterHandleElId
15232      * @param id the id of the element that will be used to initiate the drag
15233      */
15234     setOuterHandleElId: function(id) {
15235         if (typeof id !== "string") {
15236             id = Roo.id(id);
15237         }
15238         Event.on(id, "mousedown",
15239                 this.handleMouseDown, this);
15240         this.setHandleElId(id);
15241
15242         this.hasOuterHandles = true;
15243     },
15244
15245     /**
15246      * Remove all drag and drop hooks for this element
15247      * @method unreg
15248      */
15249     unreg: function() {
15250         Event.un(this.id, "mousedown",
15251                 this.handleMouseDown);
15252         this._domRef = null;
15253         this.DDM._remove(this);
15254     },
15255
15256     destroy : function(){
15257         this.unreg();
15258     },
15259
15260     /**
15261      * Returns true if this instance is locked, or the drag drop mgr is locked
15262      * (meaning that all drag/drop is disabled on the page.)
15263      * @method isLocked
15264      * @return {boolean} true if this obj or all drag/drop is locked, else
15265      * false
15266      */
15267     isLocked: function() {
15268         return (this.DDM.isLocked() || this.locked);
15269     },
15270
15271     /**
15272      * Fired when this object is clicked
15273      * @method handleMouseDown
15274      * @param {Event} e
15275      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15276      * @private
15277      */
15278     handleMouseDown: function(e, oDD){
15279         if (this.primaryButtonOnly && e.button != 0) {
15280             return;
15281         }
15282
15283         if (this.isLocked()) {
15284             return;
15285         }
15286
15287         this.DDM.refreshCache(this.groups);
15288
15289         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15290         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15291         } else {
15292             if (this.clickValidator(e)) {
15293
15294                 // set the initial element position
15295                 this.setStartPosition();
15296
15297
15298                 this.b4MouseDown(e);
15299                 this.onMouseDown(e);
15300
15301                 this.DDM.handleMouseDown(e, this);
15302
15303                 this.DDM.stopEvent(e);
15304             } else {
15305
15306
15307             }
15308         }
15309     },
15310
15311     clickValidator: function(e) {
15312         var target = e.getTarget();
15313         return ( this.isValidHandleChild(target) &&
15314                     (this.id == this.handleElId ||
15315                         this.DDM.handleWasClicked(target, this.id)) );
15316     },
15317
15318     /**
15319      * Allows you to specify a tag name that should not start a drag operation
15320      * when clicked.  This is designed to facilitate embedding links within a
15321      * drag handle that do something other than start the drag.
15322      * @method addInvalidHandleType
15323      * @param {string} tagName the type of element to exclude
15324      */
15325     addInvalidHandleType: function(tagName) {
15326         var type = tagName.toUpperCase();
15327         this.invalidHandleTypes[type] = type;
15328     },
15329
15330     /**
15331      * Lets you to specify an element id for a child of a drag handle
15332      * that should not initiate a drag
15333      * @method addInvalidHandleId
15334      * @param {string} id the element id of the element you wish to ignore
15335      */
15336     addInvalidHandleId: function(id) {
15337         if (typeof id !== "string") {
15338             id = Roo.id(id);
15339         }
15340         this.invalidHandleIds[id] = id;
15341     },
15342
15343     /**
15344      * Lets you specify a css class of elements that will not initiate a drag
15345      * @method addInvalidHandleClass
15346      * @param {string} cssClass the class of the elements you wish to ignore
15347      */
15348     addInvalidHandleClass: function(cssClass) {
15349         this.invalidHandleClasses.push(cssClass);
15350     },
15351
15352     /**
15353      * Unsets an excluded tag name set by addInvalidHandleType
15354      * @method removeInvalidHandleType
15355      * @param {string} tagName the type of element to unexclude
15356      */
15357     removeInvalidHandleType: function(tagName) {
15358         var type = tagName.toUpperCase();
15359         // this.invalidHandleTypes[type] = null;
15360         delete this.invalidHandleTypes[type];
15361     },
15362
15363     /**
15364      * Unsets an invalid handle id
15365      * @method removeInvalidHandleId
15366      * @param {string} id the id of the element to re-enable
15367      */
15368     removeInvalidHandleId: function(id) {
15369         if (typeof id !== "string") {
15370             id = Roo.id(id);
15371         }
15372         delete this.invalidHandleIds[id];
15373     },
15374
15375     /**
15376      * Unsets an invalid css class
15377      * @method removeInvalidHandleClass
15378      * @param {string} cssClass the class of the element(s) you wish to
15379      * re-enable
15380      */
15381     removeInvalidHandleClass: function(cssClass) {
15382         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15383             if (this.invalidHandleClasses[i] == cssClass) {
15384                 delete this.invalidHandleClasses[i];
15385             }
15386         }
15387     },
15388
15389     /**
15390      * Checks the tag exclusion list to see if this click should be ignored
15391      * @method isValidHandleChild
15392      * @param {HTMLElement} node the HTMLElement to evaluate
15393      * @return {boolean} true if this is a valid tag type, false if not
15394      */
15395     isValidHandleChild: function(node) {
15396
15397         var valid = true;
15398         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15399         var nodeName;
15400         try {
15401             nodeName = node.nodeName.toUpperCase();
15402         } catch(e) {
15403             nodeName = node.nodeName;
15404         }
15405         valid = valid && !this.invalidHandleTypes[nodeName];
15406         valid = valid && !this.invalidHandleIds[node.id];
15407
15408         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15409             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15410         }
15411
15412
15413         return valid;
15414
15415     },
15416
15417     /**
15418      * Create the array of horizontal tick marks if an interval was specified
15419      * in setXConstraint().
15420      * @method setXTicks
15421      * @private
15422      */
15423     setXTicks: function(iStartX, iTickSize) {
15424         this.xTicks = [];
15425         this.xTickSize = iTickSize;
15426
15427         var tickMap = {};
15428
15429         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15430             if (!tickMap[i]) {
15431                 this.xTicks[this.xTicks.length] = i;
15432                 tickMap[i] = true;
15433             }
15434         }
15435
15436         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15437             if (!tickMap[i]) {
15438                 this.xTicks[this.xTicks.length] = i;
15439                 tickMap[i] = true;
15440             }
15441         }
15442
15443         this.xTicks.sort(this.DDM.numericSort) ;
15444     },
15445
15446     /**
15447      * Create the array of vertical tick marks if an interval was specified in
15448      * setYConstraint().
15449      * @method setYTicks
15450      * @private
15451      */
15452     setYTicks: function(iStartY, iTickSize) {
15453         this.yTicks = [];
15454         this.yTickSize = iTickSize;
15455
15456         var tickMap = {};
15457
15458         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15459             if (!tickMap[i]) {
15460                 this.yTicks[this.yTicks.length] = i;
15461                 tickMap[i] = true;
15462             }
15463         }
15464
15465         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15466             if (!tickMap[i]) {
15467                 this.yTicks[this.yTicks.length] = i;
15468                 tickMap[i] = true;
15469             }
15470         }
15471
15472         this.yTicks.sort(this.DDM.numericSort) ;
15473     },
15474
15475     /**
15476      * By default, the element can be dragged any place on the screen.  Use
15477      * this method to limit the horizontal travel of the element.  Pass in
15478      * 0,0 for the parameters if you want to lock the drag to the y axis.
15479      * @method setXConstraint
15480      * @param {int} iLeft the number of pixels the element can move to the left
15481      * @param {int} iRight the number of pixels the element can move to the
15482      * right
15483      * @param {int} iTickSize optional parameter for specifying that the
15484      * element
15485      * should move iTickSize pixels at a time.
15486      */
15487     setXConstraint: function(iLeft, iRight, iTickSize) {
15488         this.leftConstraint = iLeft;
15489         this.rightConstraint = iRight;
15490
15491         this.minX = this.initPageX - iLeft;
15492         this.maxX = this.initPageX + iRight;
15493         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15494
15495         this.constrainX = true;
15496     },
15497
15498     /**
15499      * Clears any constraints applied to this instance.  Also clears ticks
15500      * since they can't exist independent of a constraint at this time.
15501      * @method clearConstraints
15502      */
15503     clearConstraints: function() {
15504         this.constrainX = false;
15505         this.constrainY = false;
15506         this.clearTicks();
15507     },
15508
15509     /**
15510      * Clears any tick interval defined for this instance
15511      * @method clearTicks
15512      */
15513     clearTicks: function() {
15514         this.xTicks = null;
15515         this.yTicks = null;
15516         this.xTickSize = 0;
15517         this.yTickSize = 0;
15518     },
15519
15520     /**
15521      * By default, the element can be dragged any place on the screen.  Set
15522      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15523      * parameters if you want to lock the drag to the x axis.
15524      * @method setYConstraint
15525      * @param {int} iUp the number of pixels the element can move up
15526      * @param {int} iDown the number of pixels the element can move down
15527      * @param {int} iTickSize optional parameter for specifying that the
15528      * element should move iTickSize pixels at a time.
15529      */
15530     setYConstraint: function(iUp, iDown, iTickSize) {
15531         this.topConstraint = iUp;
15532         this.bottomConstraint = iDown;
15533
15534         this.minY = this.initPageY - iUp;
15535         this.maxY = this.initPageY + iDown;
15536         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15537
15538         this.constrainY = true;
15539
15540     },
15541
15542     /**
15543      * resetConstraints must be called if you manually reposition a dd element.
15544      * @method resetConstraints
15545      * @param {boolean} maintainOffset
15546      */
15547     resetConstraints: function() {
15548
15549
15550         // Maintain offsets if necessary
15551         if (this.initPageX || this.initPageX === 0) {
15552             // figure out how much this thing has moved
15553             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15554             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15555
15556             this.setInitPosition(dx, dy);
15557
15558         // This is the first time we have detected the element's position
15559         } else {
15560             this.setInitPosition();
15561         }
15562
15563         if (this.constrainX) {
15564             this.setXConstraint( this.leftConstraint,
15565                                  this.rightConstraint,
15566                                  this.xTickSize        );
15567         }
15568
15569         if (this.constrainY) {
15570             this.setYConstraint( this.topConstraint,
15571                                  this.bottomConstraint,
15572                                  this.yTickSize         );
15573         }
15574     },
15575
15576     /**
15577      * Normally the drag element is moved pixel by pixel, but we can specify
15578      * that it move a number of pixels at a time.  This method resolves the
15579      * location when we have it set up like this.
15580      * @method getTick
15581      * @param {int} val where we want to place the object
15582      * @param {int[]} tickArray sorted array of valid points
15583      * @return {int} the closest tick
15584      * @private
15585      */
15586     getTick: function(val, tickArray) {
15587
15588         if (!tickArray) {
15589             // If tick interval is not defined, it is effectively 1 pixel,
15590             // so we return the value passed to us.
15591             return val;
15592         } else if (tickArray[0] >= val) {
15593             // The value is lower than the first tick, so we return the first
15594             // tick.
15595             return tickArray[0];
15596         } else {
15597             for (var i=0, len=tickArray.length; i<len; ++i) {
15598                 var next = i + 1;
15599                 if (tickArray[next] && tickArray[next] >= val) {
15600                     var diff1 = val - tickArray[i];
15601                     var diff2 = tickArray[next] - val;
15602                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15603                 }
15604             }
15605
15606             // The value is larger than the last tick, so we return the last
15607             // tick.
15608             return tickArray[tickArray.length - 1];
15609         }
15610     },
15611
15612     /**
15613      * toString method
15614      * @method toString
15615      * @return {string} string representation of the dd obj
15616      */
15617     toString: function() {
15618         return ("DragDrop " + this.id);
15619     }
15620
15621 });
15622
15623 })();
15624 /*
15625  * Based on:
15626  * Ext JS Library 1.1.1
15627  * Copyright(c) 2006-2007, Ext JS, LLC.
15628  *
15629  * Originally Released Under LGPL - original licence link has changed is not relivant.
15630  *
15631  * Fork - LGPL
15632  * <script type="text/javascript">
15633  */
15634
15635
15636 /**
15637  * The drag and drop utility provides a framework for building drag and drop
15638  * applications.  In addition to enabling drag and drop for specific elements,
15639  * the drag and drop elements are tracked by the manager class, and the
15640  * interactions between the various elements are tracked during the drag and
15641  * the implementing code is notified about these important moments.
15642  */
15643
15644 // Only load the library once.  Rewriting the manager class would orphan
15645 // existing drag and drop instances.
15646 if (!Roo.dd.DragDropMgr) {
15647
15648 /**
15649  * @class Roo.dd.DragDropMgr
15650  * DragDropMgr is a singleton that tracks the element interaction for
15651  * all DragDrop items in the window.  Generally, you will not call
15652  * this class directly, but it does have helper methods that could
15653  * be useful in your DragDrop implementations.
15654  * @singleton
15655  */
15656 Roo.dd.DragDropMgr = function() {
15657
15658     var Event = Roo.EventManager;
15659
15660     return {
15661
15662         /**
15663          * Two dimensional Array of registered DragDrop objects.  The first
15664          * dimension is the DragDrop item group, the second the DragDrop
15665          * object.
15666          * @property ids
15667          * @type {string: string}
15668          * @private
15669          * @static
15670          */
15671         ids: {},
15672
15673         /**
15674          * Array of element ids defined as drag handles.  Used to determine
15675          * if the element that generated the mousedown event is actually the
15676          * handle and not the html element itself.
15677          * @property handleIds
15678          * @type {string: string}
15679          * @private
15680          * @static
15681          */
15682         handleIds: {},
15683
15684         /**
15685          * the DragDrop object that is currently being dragged
15686          * @property dragCurrent
15687          * @type DragDrop
15688          * @private
15689          * @static
15690          **/
15691         dragCurrent: null,
15692
15693         /**
15694          * the DragDrop object(s) that are being hovered over
15695          * @property dragOvers
15696          * @type Array
15697          * @private
15698          * @static
15699          */
15700         dragOvers: {},
15701
15702         /**
15703          * the X distance between the cursor and the object being dragged
15704          * @property deltaX
15705          * @type int
15706          * @private
15707          * @static
15708          */
15709         deltaX: 0,
15710
15711         /**
15712          * the Y distance between the cursor and the object being dragged
15713          * @property deltaY
15714          * @type int
15715          * @private
15716          * @static
15717          */
15718         deltaY: 0,
15719
15720         /**
15721          * Flag to determine if we should prevent the default behavior of the
15722          * events we define. By default this is true, but this can be set to
15723          * false if you need the default behavior (not recommended)
15724          * @property preventDefault
15725          * @type boolean
15726          * @static
15727          */
15728         preventDefault: true,
15729
15730         /**
15731          * Flag to determine if we should stop the propagation of the events
15732          * we generate. This is true by default but you may want to set it to
15733          * false if the html element contains other features that require the
15734          * mouse click.
15735          * @property stopPropagation
15736          * @type boolean
15737          * @static
15738          */
15739         stopPropagation: true,
15740
15741         /**
15742          * Internal flag that is set to true when drag and drop has been
15743          * intialized
15744          * @property initialized
15745          * @private
15746          * @static
15747          */
15748         initalized: false,
15749
15750         /**
15751          * All drag and drop can be disabled.
15752          * @property locked
15753          * @private
15754          * @static
15755          */
15756         locked: false,
15757
15758         /**
15759          * Called the first time an element is registered.
15760          * @method init
15761          * @private
15762          * @static
15763          */
15764         init: function() {
15765             this.initialized = true;
15766         },
15767
15768         /**
15769          * In point mode, drag and drop interaction is defined by the
15770          * location of the cursor during the drag/drop
15771          * @property POINT
15772          * @type int
15773          * @static
15774          */
15775         POINT: 0,
15776
15777         /**
15778          * In intersect mode, drag and drop interactio nis defined by the
15779          * overlap of two or more drag and drop objects.
15780          * @property INTERSECT
15781          * @type int
15782          * @static
15783          */
15784         INTERSECT: 1,
15785
15786         /**
15787          * The current drag and drop mode.  Default: POINT
15788          * @property mode
15789          * @type int
15790          * @static
15791          */
15792         mode: 0,
15793
15794         /**
15795          * Runs method on all drag and drop objects
15796          * @method _execOnAll
15797          * @private
15798          * @static
15799          */
15800         _execOnAll: function(sMethod, args) {
15801             for (var i in this.ids) {
15802                 for (var j in this.ids[i]) {
15803                     var oDD = this.ids[i][j];
15804                     if (! this.isTypeOfDD(oDD)) {
15805                         continue;
15806                     }
15807                     oDD[sMethod].apply(oDD, args);
15808                 }
15809             }
15810         },
15811
15812         /**
15813          * Drag and drop initialization.  Sets up the global event handlers
15814          * @method _onLoad
15815          * @private
15816          * @static
15817          */
15818         _onLoad: function() {
15819
15820             this.init();
15821
15822
15823             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15824             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15825             Event.on(window,   "unload",    this._onUnload, this, true);
15826             Event.on(window,   "resize",    this._onResize, this, true);
15827             // Event.on(window,   "mouseout",    this._test);
15828
15829         },
15830
15831         /**
15832          * Reset constraints on all drag and drop objs
15833          * @method _onResize
15834          * @private
15835          * @static
15836          */
15837         _onResize: function(e) {
15838             this._execOnAll("resetConstraints", []);
15839         },
15840
15841         /**
15842          * Lock all drag and drop functionality
15843          * @method lock
15844          * @static
15845          */
15846         lock: function() { this.locked = true; },
15847
15848         /**
15849          * Unlock all drag and drop functionality
15850          * @method unlock
15851          * @static
15852          */
15853         unlock: function() { this.locked = false; },
15854
15855         /**
15856          * Is drag and drop locked?
15857          * @method isLocked
15858          * @return {boolean} True if drag and drop is locked, false otherwise.
15859          * @static
15860          */
15861         isLocked: function() { return this.locked; },
15862
15863         /**
15864          * Location cache that is set for all drag drop objects when a drag is
15865          * initiated, cleared when the drag is finished.
15866          * @property locationCache
15867          * @private
15868          * @static
15869          */
15870         locationCache: {},
15871
15872         /**
15873          * Set useCache to false if you want to force object the lookup of each
15874          * drag and drop linked element constantly during a drag.
15875          * @property useCache
15876          * @type boolean
15877          * @static
15878          */
15879         useCache: true,
15880
15881         /**
15882          * The number of pixels that the mouse needs to move after the
15883          * mousedown before the drag is initiated.  Default=3;
15884          * @property clickPixelThresh
15885          * @type int
15886          * @static
15887          */
15888         clickPixelThresh: 3,
15889
15890         /**
15891          * The number of milliseconds after the mousedown event to initiate the
15892          * drag if we don't get a mouseup event. Default=1000
15893          * @property clickTimeThresh
15894          * @type int
15895          * @static
15896          */
15897         clickTimeThresh: 350,
15898
15899         /**
15900          * Flag that indicates that either the drag pixel threshold or the
15901          * mousdown time threshold has been met
15902          * @property dragThreshMet
15903          * @type boolean
15904          * @private
15905          * @static
15906          */
15907         dragThreshMet: false,
15908
15909         /**
15910          * Timeout used for the click time threshold
15911          * @property clickTimeout
15912          * @type Object
15913          * @private
15914          * @static
15915          */
15916         clickTimeout: null,
15917
15918         /**
15919          * The X position of the mousedown event stored for later use when a
15920          * drag threshold is met.
15921          * @property startX
15922          * @type int
15923          * @private
15924          * @static
15925          */
15926         startX: 0,
15927
15928         /**
15929          * The Y position of the mousedown event stored for later use when a
15930          * drag threshold is met.
15931          * @property startY
15932          * @type int
15933          * @private
15934          * @static
15935          */
15936         startY: 0,
15937
15938         /**
15939          * Each DragDrop instance must be registered with the DragDropMgr.
15940          * This is executed in DragDrop.init()
15941          * @method regDragDrop
15942          * @param {DragDrop} oDD the DragDrop object to register
15943          * @param {String} sGroup the name of the group this element belongs to
15944          * @static
15945          */
15946         regDragDrop: function(oDD, sGroup) {
15947             if (!this.initialized) { this.init(); }
15948
15949             if (!this.ids[sGroup]) {
15950                 this.ids[sGroup] = {};
15951             }
15952             this.ids[sGroup][oDD.id] = oDD;
15953         },
15954
15955         /**
15956          * Removes the supplied dd instance from the supplied group. Executed
15957          * by DragDrop.removeFromGroup, so don't call this function directly.
15958          * @method removeDDFromGroup
15959          * @private
15960          * @static
15961          */
15962         removeDDFromGroup: function(oDD, sGroup) {
15963             if (!this.ids[sGroup]) {
15964                 this.ids[sGroup] = {};
15965             }
15966
15967             var obj = this.ids[sGroup];
15968             if (obj && obj[oDD.id]) {
15969                 delete obj[oDD.id];
15970             }
15971         },
15972
15973         /**
15974          * Unregisters a drag and drop item.  This is executed in
15975          * DragDrop.unreg, use that method instead of calling this directly.
15976          * @method _remove
15977          * @private
15978          * @static
15979          */
15980         _remove: function(oDD) {
15981             for (var g in oDD.groups) {
15982                 if (g && this.ids[g][oDD.id]) {
15983                     delete this.ids[g][oDD.id];
15984                 }
15985             }
15986             delete this.handleIds[oDD.id];
15987         },
15988
15989         /**
15990          * Each DragDrop handle element must be registered.  This is done
15991          * automatically when executing DragDrop.setHandleElId()
15992          * @method regHandle
15993          * @param {String} sDDId the DragDrop id this element is a handle for
15994          * @param {String} sHandleId the id of the element that is the drag
15995          * handle
15996          * @static
15997          */
15998         regHandle: function(sDDId, sHandleId) {
15999             if (!this.handleIds[sDDId]) {
16000                 this.handleIds[sDDId] = {};
16001             }
16002             this.handleIds[sDDId][sHandleId] = sHandleId;
16003         },
16004
16005         /**
16006          * Utility function to determine if a given element has been
16007          * registered as a drag drop item.
16008          * @method isDragDrop
16009          * @param {String} id the element id to check
16010          * @return {boolean} true if this element is a DragDrop item,
16011          * false otherwise
16012          * @static
16013          */
16014         isDragDrop: function(id) {
16015             return ( this.getDDById(id) ) ? true : false;
16016         },
16017
16018         /**
16019          * Returns the drag and drop instances that are in all groups the
16020          * passed in instance belongs to.
16021          * @method getRelated
16022          * @param {DragDrop} p_oDD the obj to get related data for
16023          * @param {boolean} bTargetsOnly if true, only return targetable objs
16024          * @return {DragDrop[]} the related instances
16025          * @static
16026          */
16027         getRelated: function(p_oDD, bTargetsOnly) {
16028             var oDDs = [];
16029             for (var i in p_oDD.groups) {
16030                 for (j in this.ids[i]) {
16031                     var dd = this.ids[i][j];
16032                     if (! this.isTypeOfDD(dd)) {
16033                         continue;
16034                     }
16035                     if (!bTargetsOnly || dd.isTarget) {
16036                         oDDs[oDDs.length] = dd;
16037                     }
16038                 }
16039             }
16040
16041             return oDDs;
16042         },
16043
16044         /**
16045          * Returns true if the specified dd target is a legal target for
16046          * the specifice drag obj
16047          * @method isLegalTarget
16048          * @param {DragDrop} the drag obj
16049          * @param {DragDrop} the target
16050          * @return {boolean} true if the target is a legal target for the
16051          * dd obj
16052          * @static
16053          */
16054         isLegalTarget: function (oDD, oTargetDD) {
16055             var targets = this.getRelated(oDD, true);
16056             for (var i=0, len=targets.length;i<len;++i) {
16057                 if (targets[i].id == oTargetDD.id) {
16058                     return true;
16059                 }
16060             }
16061
16062             return false;
16063         },
16064
16065         /**
16066          * My goal is to be able to transparently determine if an object is
16067          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16068          * returns "object", oDD.constructor.toString() always returns
16069          * "DragDrop" and not the name of the subclass.  So for now it just
16070          * evaluates a well-known variable in DragDrop.
16071          * @method isTypeOfDD
16072          * @param {Object} the object to evaluate
16073          * @return {boolean} true if typeof oDD = DragDrop
16074          * @static
16075          */
16076         isTypeOfDD: function (oDD) {
16077             return (oDD && oDD.__ygDragDrop);
16078         },
16079
16080         /**
16081          * Utility function to determine if a given element has been
16082          * registered as a drag drop handle for the given Drag Drop object.
16083          * @method isHandle
16084          * @param {String} id the element id to check
16085          * @return {boolean} true if this element is a DragDrop handle, false
16086          * otherwise
16087          * @static
16088          */
16089         isHandle: function(sDDId, sHandleId) {
16090             return ( this.handleIds[sDDId] &&
16091                             this.handleIds[sDDId][sHandleId] );
16092         },
16093
16094         /**
16095          * Returns the DragDrop instance for a given id
16096          * @method getDDById
16097          * @param {String} id the id of the DragDrop object
16098          * @return {DragDrop} the drag drop object, null if it is not found
16099          * @static
16100          */
16101         getDDById: function(id) {
16102             for (var i in this.ids) {
16103                 if (this.ids[i][id]) {
16104                     return this.ids[i][id];
16105                 }
16106             }
16107             return null;
16108         },
16109
16110         /**
16111          * Fired after a registered DragDrop object gets the mousedown event.
16112          * Sets up the events required to track the object being dragged
16113          * @method handleMouseDown
16114          * @param {Event} e the event
16115          * @param oDD the DragDrop object being dragged
16116          * @private
16117          * @static
16118          */
16119         handleMouseDown: function(e, oDD) {
16120             if(Roo.QuickTips){
16121                 Roo.QuickTips.disable();
16122             }
16123             this.currentTarget = e.getTarget();
16124
16125             this.dragCurrent = oDD;
16126
16127             var el = oDD.getEl();
16128
16129             // track start position
16130             this.startX = e.getPageX();
16131             this.startY = e.getPageY();
16132
16133             this.deltaX = this.startX - el.offsetLeft;
16134             this.deltaY = this.startY - el.offsetTop;
16135
16136             this.dragThreshMet = false;
16137
16138             this.clickTimeout = setTimeout(
16139                     function() {
16140                         var DDM = Roo.dd.DDM;
16141                         DDM.startDrag(DDM.startX, DDM.startY);
16142                     },
16143                     this.clickTimeThresh );
16144         },
16145
16146         /**
16147          * Fired when either the drag pixel threshol or the mousedown hold
16148          * time threshold has been met.
16149          * @method startDrag
16150          * @param x {int} the X position of the original mousedown
16151          * @param y {int} the Y position of the original mousedown
16152          * @static
16153          */
16154         startDrag: function(x, y) {
16155             clearTimeout(this.clickTimeout);
16156             if (this.dragCurrent) {
16157                 this.dragCurrent.b4StartDrag(x, y);
16158                 this.dragCurrent.startDrag(x, y);
16159             }
16160             this.dragThreshMet = true;
16161         },
16162
16163         /**
16164          * Internal function to handle the mouseup event.  Will be invoked
16165          * from the context of the document.
16166          * @method handleMouseUp
16167          * @param {Event} e the event
16168          * @private
16169          * @static
16170          */
16171         handleMouseUp: function(e) {
16172
16173             if(Roo.QuickTips){
16174                 Roo.QuickTips.enable();
16175             }
16176             if (! this.dragCurrent) {
16177                 return;
16178             }
16179
16180             clearTimeout(this.clickTimeout);
16181
16182             if (this.dragThreshMet) {
16183                 this.fireEvents(e, true);
16184             } else {
16185             }
16186
16187             this.stopDrag(e);
16188
16189             this.stopEvent(e);
16190         },
16191
16192         /**
16193          * Utility to stop event propagation and event default, if these
16194          * features are turned on.
16195          * @method stopEvent
16196          * @param {Event} e the event as returned by this.getEvent()
16197          * @static
16198          */
16199         stopEvent: function(e){
16200             if(this.stopPropagation) {
16201                 e.stopPropagation();
16202             }
16203
16204             if (this.preventDefault) {
16205                 e.preventDefault();
16206             }
16207         },
16208
16209         /**
16210          * Internal function to clean up event handlers after the drag
16211          * operation is complete
16212          * @method stopDrag
16213          * @param {Event} e the event
16214          * @private
16215          * @static
16216          */
16217         stopDrag: function(e) {
16218             // Fire the drag end event for the item that was dragged
16219             if (this.dragCurrent) {
16220                 if (this.dragThreshMet) {
16221                     this.dragCurrent.b4EndDrag(e);
16222                     this.dragCurrent.endDrag(e);
16223                 }
16224
16225                 this.dragCurrent.onMouseUp(e);
16226             }
16227
16228             this.dragCurrent = null;
16229             this.dragOvers = {};
16230         },
16231
16232         /**
16233          * Internal function to handle the mousemove event.  Will be invoked
16234          * from the context of the html element.
16235          *
16236          * @TODO figure out what we can do about mouse events lost when the
16237          * user drags objects beyond the window boundary.  Currently we can
16238          * detect this in internet explorer by verifying that the mouse is
16239          * down during the mousemove event.  Firefox doesn't give us the
16240          * button state on the mousemove event.
16241          * @method handleMouseMove
16242          * @param {Event} e the event
16243          * @private
16244          * @static
16245          */
16246         handleMouseMove: function(e) {
16247             if (! this.dragCurrent) {
16248                 return true;
16249             }
16250
16251             // var button = e.which || e.button;
16252
16253             // check for IE mouseup outside of page boundary
16254             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16255                 this.stopEvent(e);
16256                 return this.handleMouseUp(e);
16257             }
16258
16259             if (!this.dragThreshMet) {
16260                 var diffX = Math.abs(this.startX - e.getPageX());
16261                 var diffY = Math.abs(this.startY - e.getPageY());
16262                 if (diffX > this.clickPixelThresh ||
16263                             diffY > this.clickPixelThresh) {
16264                     this.startDrag(this.startX, this.startY);
16265                 }
16266             }
16267
16268             if (this.dragThreshMet) {
16269                 this.dragCurrent.b4Drag(e);
16270                 this.dragCurrent.onDrag(e);
16271                 if(!this.dragCurrent.moveOnly){
16272                     this.fireEvents(e, false);
16273                 }
16274             }
16275
16276             this.stopEvent(e);
16277
16278             return true;
16279         },
16280
16281         /**
16282          * Iterates over all of the DragDrop elements to find ones we are
16283          * hovering over or dropping on
16284          * @method fireEvents
16285          * @param {Event} e the event
16286          * @param {boolean} isDrop is this a drop op or a mouseover op?
16287          * @private
16288          * @static
16289          */
16290         fireEvents: function(e, isDrop) {
16291             var dc = this.dragCurrent;
16292
16293             // If the user did the mouse up outside of the window, we could
16294             // get here even though we have ended the drag.
16295             if (!dc || dc.isLocked()) {
16296                 return;
16297             }
16298
16299             var pt = e.getPoint();
16300
16301             // cache the previous dragOver array
16302             var oldOvers = [];
16303
16304             var outEvts   = [];
16305             var overEvts  = [];
16306             var dropEvts  = [];
16307             var enterEvts = [];
16308
16309             // Check to see if the object(s) we were hovering over is no longer
16310             // being hovered over so we can fire the onDragOut event
16311             for (var i in this.dragOvers) {
16312
16313                 var ddo = this.dragOvers[i];
16314
16315                 if (! this.isTypeOfDD(ddo)) {
16316                     continue;
16317                 }
16318
16319                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16320                     outEvts.push( ddo );
16321                 }
16322
16323                 oldOvers[i] = true;
16324                 delete this.dragOvers[i];
16325             }
16326
16327             for (var sGroup in dc.groups) {
16328
16329                 if ("string" != typeof sGroup) {
16330                     continue;
16331                 }
16332
16333                 for (i in this.ids[sGroup]) {
16334                     var oDD = this.ids[sGroup][i];
16335                     if (! this.isTypeOfDD(oDD)) {
16336                         continue;
16337                     }
16338
16339                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16340                         if (this.isOverTarget(pt, oDD, this.mode)) {
16341                             // look for drop interactions
16342                             if (isDrop) {
16343                                 dropEvts.push( oDD );
16344                             // look for drag enter and drag over interactions
16345                             } else {
16346
16347                                 // initial drag over: dragEnter fires
16348                                 if (!oldOvers[oDD.id]) {
16349                                     enterEvts.push( oDD );
16350                                 // subsequent drag overs: dragOver fires
16351                                 } else {
16352                                     overEvts.push( oDD );
16353                                 }
16354
16355                                 this.dragOvers[oDD.id] = oDD;
16356                             }
16357                         }
16358                     }
16359                 }
16360             }
16361
16362             if (this.mode) {
16363                 if (outEvts.length) {
16364                     dc.b4DragOut(e, outEvts);
16365                     dc.onDragOut(e, outEvts);
16366                 }
16367
16368                 if (enterEvts.length) {
16369                     dc.onDragEnter(e, enterEvts);
16370                 }
16371
16372                 if (overEvts.length) {
16373                     dc.b4DragOver(e, overEvts);
16374                     dc.onDragOver(e, overEvts);
16375                 }
16376
16377                 if (dropEvts.length) {
16378                     dc.b4DragDrop(e, dropEvts);
16379                     dc.onDragDrop(e, dropEvts);
16380                 }
16381
16382             } else {
16383                 // fire dragout events
16384                 var len = 0;
16385                 for (i=0, len=outEvts.length; i<len; ++i) {
16386                     dc.b4DragOut(e, outEvts[i].id);
16387                     dc.onDragOut(e, outEvts[i].id);
16388                 }
16389
16390                 // fire enter events
16391                 for (i=0,len=enterEvts.length; i<len; ++i) {
16392                     // dc.b4DragEnter(e, oDD.id);
16393                     dc.onDragEnter(e, enterEvts[i].id);
16394                 }
16395
16396                 // fire over events
16397                 for (i=0,len=overEvts.length; i<len; ++i) {
16398                     dc.b4DragOver(e, overEvts[i].id);
16399                     dc.onDragOver(e, overEvts[i].id);
16400                 }
16401
16402                 // fire drop events
16403                 for (i=0, len=dropEvts.length; i<len; ++i) {
16404                     dc.b4DragDrop(e, dropEvts[i].id);
16405                     dc.onDragDrop(e, dropEvts[i].id);
16406                 }
16407
16408             }
16409
16410             // notify about a drop that did not find a target
16411             if (isDrop && !dropEvts.length) {
16412                 dc.onInvalidDrop(e);
16413             }
16414
16415         },
16416
16417         /**
16418          * Helper function for getting the best match from the list of drag
16419          * and drop objects returned by the drag and drop events when we are
16420          * in INTERSECT mode.  It returns either the first object that the
16421          * cursor is over, or the object that has the greatest overlap with
16422          * the dragged element.
16423          * @method getBestMatch
16424          * @param  {DragDrop[]} dds The array of drag and drop objects
16425          * targeted
16426          * @return {DragDrop}       The best single match
16427          * @static
16428          */
16429         getBestMatch: function(dds) {
16430             var winner = null;
16431             // Return null if the input is not what we expect
16432             //if (!dds || !dds.length || dds.length == 0) {
16433                // winner = null;
16434             // If there is only one item, it wins
16435             //} else if (dds.length == 1) {
16436
16437             var len = dds.length;
16438
16439             if (len == 1) {
16440                 winner = dds[0];
16441             } else {
16442                 // Loop through the targeted items
16443                 for (var i=0; i<len; ++i) {
16444                     var dd = dds[i];
16445                     // If the cursor is over the object, it wins.  If the
16446                     // cursor is over multiple matches, the first one we come
16447                     // to wins.
16448                     if (dd.cursorIsOver) {
16449                         winner = dd;
16450                         break;
16451                     // Otherwise the object with the most overlap wins
16452                     } else {
16453                         if (!winner ||
16454                             winner.overlap.getArea() < dd.overlap.getArea()) {
16455                             winner = dd;
16456                         }
16457                     }
16458                 }
16459             }
16460
16461             return winner;
16462         },
16463
16464         /**
16465          * Refreshes the cache of the top-left and bottom-right points of the
16466          * drag and drop objects in the specified group(s).  This is in the
16467          * format that is stored in the drag and drop instance, so typical
16468          * usage is:
16469          * <code>
16470          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16471          * </code>
16472          * Alternatively:
16473          * <code>
16474          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16475          * </code>
16476          * @TODO this really should be an indexed array.  Alternatively this
16477          * method could accept both.
16478          * @method refreshCache
16479          * @param {Object} groups an associative array of groups to refresh
16480          * @static
16481          */
16482         refreshCache: function(groups) {
16483             for (var sGroup in groups) {
16484                 if ("string" != typeof sGroup) {
16485                     continue;
16486                 }
16487                 for (var i in this.ids[sGroup]) {
16488                     var oDD = this.ids[sGroup][i];
16489
16490                     if (this.isTypeOfDD(oDD)) {
16491                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16492                         var loc = this.getLocation(oDD);
16493                         if (loc) {
16494                             this.locationCache[oDD.id] = loc;
16495                         } else {
16496                             delete this.locationCache[oDD.id];
16497                             // this will unregister the drag and drop object if
16498                             // the element is not in a usable state
16499                             // oDD.unreg();
16500                         }
16501                     }
16502                 }
16503             }
16504         },
16505
16506         /**
16507          * This checks to make sure an element exists and is in the DOM.  The
16508          * main purpose is to handle cases where innerHTML is used to remove
16509          * drag and drop objects from the DOM.  IE provides an 'unspecified
16510          * error' when trying to access the offsetParent of such an element
16511          * @method verifyEl
16512          * @param {HTMLElement} el the element to check
16513          * @return {boolean} true if the element looks usable
16514          * @static
16515          */
16516         verifyEl: function(el) {
16517             if (el) {
16518                 var parent;
16519                 if(Roo.isIE){
16520                     try{
16521                         parent = el.offsetParent;
16522                     }catch(e){}
16523                 }else{
16524                     parent = el.offsetParent;
16525                 }
16526                 if (parent) {
16527                     return true;
16528                 }
16529             }
16530
16531             return false;
16532         },
16533
16534         /**
16535          * Returns a Region object containing the drag and drop element's position
16536          * and size, including the padding configured for it
16537          * @method getLocation
16538          * @param {DragDrop} oDD the drag and drop object to get the
16539          *                       location for
16540          * @return {Roo.lib.Region} a Region object representing the total area
16541          *                             the element occupies, including any padding
16542          *                             the instance is configured for.
16543          * @static
16544          */
16545         getLocation: function(oDD) {
16546             if (! this.isTypeOfDD(oDD)) {
16547                 return null;
16548             }
16549
16550             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16551
16552             try {
16553                 pos= Roo.lib.Dom.getXY(el);
16554             } catch (e) { }
16555
16556             if (!pos) {
16557                 return null;
16558             }
16559
16560             x1 = pos[0];
16561             x2 = x1 + el.offsetWidth;
16562             y1 = pos[1];
16563             y2 = y1 + el.offsetHeight;
16564
16565             t = y1 - oDD.padding[0];
16566             r = x2 + oDD.padding[1];
16567             b = y2 + oDD.padding[2];
16568             l = x1 - oDD.padding[3];
16569
16570             return new Roo.lib.Region( t, r, b, l );
16571         },
16572
16573         /**
16574          * Checks the cursor location to see if it over the target
16575          * @method isOverTarget
16576          * @param {Roo.lib.Point} pt The point to evaluate
16577          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16578          * @return {boolean} true if the mouse is over the target
16579          * @private
16580          * @static
16581          */
16582         isOverTarget: function(pt, oTarget, intersect) {
16583             // use cache if available
16584             var loc = this.locationCache[oTarget.id];
16585             if (!loc || !this.useCache) {
16586                 loc = this.getLocation(oTarget);
16587                 this.locationCache[oTarget.id] = loc;
16588
16589             }
16590
16591             if (!loc) {
16592                 return false;
16593             }
16594
16595             oTarget.cursorIsOver = loc.contains( pt );
16596
16597             // DragDrop is using this as a sanity check for the initial mousedown
16598             // in this case we are done.  In POINT mode, if the drag obj has no
16599             // contraints, we are also done. Otherwise we need to evaluate the
16600             // location of the target as related to the actual location of the
16601             // dragged element.
16602             var dc = this.dragCurrent;
16603             if (!dc || !dc.getTargetCoord ||
16604                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16605                 return oTarget.cursorIsOver;
16606             }
16607
16608             oTarget.overlap = null;
16609
16610             // Get the current location of the drag element, this is the
16611             // location of the mouse event less the delta that represents
16612             // where the original mousedown happened on the element.  We
16613             // need to consider constraints and ticks as well.
16614             var pos = dc.getTargetCoord(pt.x, pt.y);
16615
16616             var el = dc.getDragEl();
16617             var curRegion = new Roo.lib.Region( pos.y,
16618                                                    pos.x + el.offsetWidth,
16619                                                    pos.y + el.offsetHeight,
16620                                                    pos.x );
16621
16622             var overlap = curRegion.intersect(loc);
16623
16624             if (overlap) {
16625                 oTarget.overlap = overlap;
16626                 return (intersect) ? true : oTarget.cursorIsOver;
16627             } else {
16628                 return false;
16629             }
16630         },
16631
16632         /**
16633          * unload event handler
16634          * @method _onUnload
16635          * @private
16636          * @static
16637          */
16638         _onUnload: function(e, me) {
16639             Roo.dd.DragDropMgr.unregAll();
16640         },
16641
16642         /**
16643          * Cleans up the drag and drop events and objects.
16644          * @method unregAll
16645          * @private
16646          * @static
16647          */
16648         unregAll: function() {
16649
16650             if (this.dragCurrent) {
16651                 this.stopDrag();
16652                 this.dragCurrent = null;
16653             }
16654
16655             this._execOnAll("unreg", []);
16656
16657             for (i in this.elementCache) {
16658                 delete this.elementCache[i];
16659             }
16660
16661             this.elementCache = {};
16662             this.ids = {};
16663         },
16664
16665         /**
16666          * A cache of DOM elements
16667          * @property elementCache
16668          * @private
16669          * @static
16670          */
16671         elementCache: {},
16672
16673         /**
16674          * Get the wrapper for the DOM element specified
16675          * @method getElWrapper
16676          * @param {String} id the id of the element to get
16677          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16678          * @private
16679          * @deprecated This wrapper isn't that useful
16680          * @static
16681          */
16682         getElWrapper: function(id) {
16683             var oWrapper = this.elementCache[id];
16684             if (!oWrapper || !oWrapper.el) {
16685                 oWrapper = this.elementCache[id] =
16686                     new this.ElementWrapper(Roo.getDom(id));
16687             }
16688             return oWrapper;
16689         },
16690
16691         /**
16692          * Returns the actual DOM element
16693          * @method getElement
16694          * @param {String} id the id of the elment to get
16695          * @return {Object} The element
16696          * @deprecated use Roo.getDom instead
16697          * @static
16698          */
16699         getElement: function(id) {
16700             return Roo.getDom(id);
16701         },
16702
16703         /**
16704          * Returns the style property for the DOM element (i.e.,
16705          * document.getElById(id).style)
16706          * @method getCss
16707          * @param {String} id the id of the elment to get
16708          * @return {Object} The style property of the element
16709          * @deprecated use Roo.getDom instead
16710          * @static
16711          */
16712         getCss: function(id) {
16713             var el = Roo.getDom(id);
16714             return (el) ? el.style : null;
16715         },
16716
16717         /**
16718          * Inner class for cached elements
16719          * @class DragDropMgr.ElementWrapper
16720          * @for DragDropMgr
16721          * @private
16722          * @deprecated
16723          */
16724         ElementWrapper: function(el) {
16725                 /**
16726                  * The element
16727                  * @property el
16728                  */
16729                 this.el = el || null;
16730                 /**
16731                  * The element id
16732                  * @property id
16733                  */
16734                 this.id = this.el && el.id;
16735                 /**
16736                  * A reference to the style property
16737                  * @property css
16738                  */
16739                 this.css = this.el && el.style;
16740             },
16741
16742         /**
16743          * Returns the X position of an html element
16744          * @method getPosX
16745          * @param el the element for which to get the position
16746          * @return {int} the X coordinate
16747          * @for DragDropMgr
16748          * @deprecated use Roo.lib.Dom.getX instead
16749          * @static
16750          */
16751         getPosX: function(el) {
16752             return Roo.lib.Dom.getX(el);
16753         },
16754
16755         /**
16756          * Returns the Y position of an html element
16757          * @method getPosY
16758          * @param el the element for which to get the position
16759          * @return {int} the Y coordinate
16760          * @deprecated use Roo.lib.Dom.getY instead
16761          * @static
16762          */
16763         getPosY: function(el) {
16764             return Roo.lib.Dom.getY(el);
16765         },
16766
16767         /**
16768          * Swap two nodes.  In IE, we use the native method, for others we
16769          * emulate the IE behavior
16770          * @method swapNode
16771          * @param n1 the first node to swap
16772          * @param n2 the other node to swap
16773          * @static
16774          */
16775         swapNode: function(n1, n2) {
16776             if (n1.swapNode) {
16777                 n1.swapNode(n2);
16778             } else {
16779                 var p = n2.parentNode;
16780                 var s = n2.nextSibling;
16781
16782                 if (s == n1) {
16783                     p.insertBefore(n1, n2);
16784                 } else if (n2 == n1.nextSibling) {
16785                     p.insertBefore(n2, n1);
16786                 } else {
16787                     n1.parentNode.replaceChild(n2, n1);
16788                     p.insertBefore(n1, s);
16789                 }
16790             }
16791         },
16792
16793         /**
16794          * Returns the current scroll position
16795          * @method getScroll
16796          * @private
16797          * @static
16798          */
16799         getScroll: function () {
16800             var t, l, dde=document.documentElement, db=document.body;
16801             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16802                 t = dde.scrollTop;
16803                 l = dde.scrollLeft;
16804             } else if (db) {
16805                 t = db.scrollTop;
16806                 l = db.scrollLeft;
16807             } else {
16808
16809             }
16810             return { top: t, left: l };
16811         },
16812
16813         /**
16814          * Returns the specified element style property
16815          * @method getStyle
16816          * @param {HTMLElement} el          the element
16817          * @param {string}      styleProp   the style property
16818          * @return {string} The value of the style property
16819          * @deprecated use Roo.lib.Dom.getStyle
16820          * @static
16821          */
16822         getStyle: function(el, styleProp) {
16823             return Roo.fly(el).getStyle(styleProp);
16824         },
16825
16826         /**
16827          * Gets the scrollTop
16828          * @method getScrollTop
16829          * @return {int} the document's scrollTop
16830          * @static
16831          */
16832         getScrollTop: function () { return this.getScroll().top; },
16833
16834         /**
16835          * Gets the scrollLeft
16836          * @method getScrollLeft
16837          * @return {int} the document's scrollTop
16838          * @static
16839          */
16840         getScrollLeft: function () { return this.getScroll().left; },
16841
16842         /**
16843          * Sets the x/y position of an element to the location of the
16844          * target element.
16845          * @method moveToEl
16846          * @param {HTMLElement} moveEl      The element to move
16847          * @param {HTMLElement} targetEl    The position reference element
16848          * @static
16849          */
16850         moveToEl: function (moveEl, targetEl) {
16851             var aCoord = Roo.lib.Dom.getXY(targetEl);
16852             Roo.lib.Dom.setXY(moveEl, aCoord);
16853         },
16854
16855         /**
16856          * Numeric array sort function
16857          * @method numericSort
16858          * @static
16859          */
16860         numericSort: function(a, b) { return (a - b); },
16861
16862         /**
16863          * Internal counter
16864          * @property _timeoutCount
16865          * @private
16866          * @static
16867          */
16868         _timeoutCount: 0,
16869
16870         /**
16871          * Trying to make the load order less important.  Without this we get
16872          * an error if this file is loaded before the Event Utility.
16873          * @method _addListeners
16874          * @private
16875          * @static
16876          */
16877         _addListeners: function() {
16878             var DDM = Roo.dd.DDM;
16879             if ( Roo.lib.Event && document ) {
16880                 DDM._onLoad();
16881             } else {
16882                 if (DDM._timeoutCount > 2000) {
16883                 } else {
16884                     setTimeout(DDM._addListeners, 10);
16885                     if (document && document.body) {
16886                         DDM._timeoutCount += 1;
16887                     }
16888                 }
16889             }
16890         },
16891
16892         /**
16893          * Recursively searches the immediate parent and all child nodes for
16894          * the handle element in order to determine wheter or not it was
16895          * clicked.
16896          * @method handleWasClicked
16897          * @param node the html element to inspect
16898          * @static
16899          */
16900         handleWasClicked: function(node, id) {
16901             if (this.isHandle(id, node.id)) {
16902                 return true;
16903             } else {
16904                 // check to see if this is a text node child of the one we want
16905                 var p = node.parentNode;
16906
16907                 while (p) {
16908                     if (this.isHandle(id, p.id)) {
16909                         return true;
16910                     } else {
16911                         p = p.parentNode;
16912                     }
16913                 }
16914             }
16915
16916             return false;
16917         }
16918
16919     };
16920
16921 }();
16922
16923 // shorter alias, save a few bytes
16924 Roo.dd.DDM = Roo.dd.DragDropMgr;
16925 Roo.dd.DDM._addListeners();
16926
16927 }/*
16928  * Based on:
16929  * Ext JS Library 1.1.1
16930  * Copyright(c) 2006-2007, Ext JS, LLC.
16931  *
16932  * Originally Released Under LGPL - original licence link has changed is not relivant.
16933  *
16934  * Fork - LGPL
16935  * <script type="text/javascript">
16936  */
16937
16938 /**
16939  * @class Roo.dd.DD
16940  * A DragDrop implementation where the linked element follows the
16941  * mouse cursor during a drag.
16942  * @extends Roo.dd.DragDrop
16943  * @constructor
16944  * @param {String} id the id of the linked element
16945  * @param {String} sGroup the group of related DragDrop items
16946  * @param {object} config an object containing configurable attributes
16947  *                Valid properties for DD:
16948  *                    scroll
16949  */
16950 Roo.dd.DD = function(id, sGroup, config) {
16951     if (id) {
16952         this.init(id, sGroup, config);
16953     }
16954 };
16955
16956 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16957
16958     /**
16959      * When set to true, the utility automatically tries to scroll the browser
16960      * window wehn a drag and drop element is dragged near the viewport boundary.
16961      * Defaults to true.
16962      * @property scroll
16963      * @type boolean
16964      */
16965     scroll: true,
16966
16967     /**
16968      * Sets the pointer offset to the distance between the linked element's top
16969      * left corner and the location the element was clicked
16970      * @method autoOffset
16971      * @param {int} iPageX the X coordinate of the click
16972      * @param {int} iPageY the Y coordinate of the click
16973      */
16974     autoOffset: function(iPageX, iPageY) {
16975         var x = iPageX - this.startPageX;
16976         var y = iPageY - this.startPageY;
16977         this.setDelta(x, y);
16978     },
16979
16980     /**
16981      * Sets the pointer offset.  You can call this directly to force the
16982      * offset to be in a particular location (e.g., pass in 0,0 to set it
16983      * to the center of the object)
16984      * @method setDelta
16985      * @param {int} iDeltaX the distance from the left
16986      * @param {int} iDeltaY the distance from the top
16987      */
16988     setDelta: function(iDeltaX, iDeltaY) {
16989         this.deltaX = iDeltaX;
16990         this.deltaY = iDeltaY;
16991     },
16992
16993     /**
16994      * Sets the drag element to the location of the mousedown or click event,
16995      * maintaining the cursor location relative to the location on the element
16996      * that was clicked.  Override this if you want to place the element in a
16997      * location other than where the cursor is.
16998      * @method setDragElPos
16999      * @param {int} iPageX the X coordinate of the mousedown or drag event
17000      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17001      */
17002     setDragElPos: function(iPageX, iPageY) {
17003         // the first time we do this, we are going to check to make sure
17004         // the element has css positioning
17005
17006         var el = this.getDragEl();
17007         this.alignElWithMouse(el, iPageX, iPageY);
17008     },
17009
17010     /**
17011      * Sets the element to the location of the mousedown or click event,
17012      * maintaining the cursor location relative to the location on the element
17013      * that was clicked.  Override this if you want to place the element in a
17014      * location other than where the cursor is.
17015      * @method alignElWithMouse
17016      * @param {HTMLElement} el the element to move
17017      * @param {int} iPageX the X coordinate of the mousedown or drag event
17018      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17019      */
17020     alignElWithMouse: function(el, iPageX, iPageY) {
17021         var oCoord = this.getTargetCoord(iPageX, iPageY);
17022         var fly = el.dom ? el : Roo.fly(el);
17023         if (!this.deltaSetXY) {
17024             var aCoord = [oCoord.x, oCoord.y];
17025             fly.setXY(aCoord);
17026             var newLeft = fly.getLeft(true);
17027             var newTop  = fly.getTop(true);
17028             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17029         } else {
17030             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17031         }
17032
17033         this.cachePosition(oCoord.x, oCoord.y);
17034         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17035         return oCoord;
17036     },
17037
17038     /**
17039      * Saves the most recent position so that we can reset the constraints and
17040      * tick marks on-demand.  We need to know this so that we can calculate the
17041      * number of pixels the element is offset from its original position.
17042      * @method cachePosition
17043      * @param iPageX the current x position (optional, this just makes it so we
17044      * don't have to look it up again)
17045      * @param iPageY the current y position (optional, this just makes it so we
17046      * don't have to look it up again)
17047      */
17048     cachePosition: function(iPageX, iPageY) {
17049         if (iPageX) {
17050             this.lastPageX = iPageX;
17051             this.lastPageY = iPageY;
17052         } else {
17053             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17054             this.lastPageX = aCoord[0];
17055             this.lastPageY = aCoord[1];
17056         }
17057     },
17058
17059     /**
17060      * Auto-scroll the window if the dragged object has been moved beyond the
17061      * visible window boundary.
17062      * @method autoScroll
17063      * @param {int} x the drag element's x position
17064      * @param {int} y the drag element's y position
17065      * @param {int} h the height of the drag element
17066      * @param {int} w the width of the drag element
17067      * @private
17068      */
17069     autoScroll: function(x, y, h, w) {
17070
17071         if (this.scroll) {
17072             // The client height
17073             var clientH = Roo.lib.Dom.getViewWidth();
17074
17075             // The client width
17076             var clientW = Roo.lib.Dom.getViewHeight();
17077
17078             // The amt scrolled down
17079             var st = this.DDM.getScrollTop();
17080
17081             // The amt scrolled right
17082             var sl = this.DDM.getScrollLeft();
17083
17084             // Location of the bottom of the element
17085             var bot = h + y;
17086
17087             // Location of the right of the element
17088             var right = w + x;
17089
17090             // The distance from the cursor to the bottom of the visible area,
17091             // adjusted so that we don't scroll if the cursor is beyond the
17092             // element drag constraints
17093             var toBot = (clientH + st - y - this.deltaY);
17094
17095             // The distance from the cursor to the right of the visible area
17096             var toRight = (clientW + sl - x - this.deltaX);
17097
17098
17099             // How close to the edge the cursor must be before we scroll
17100             // var thresh = (document.all) ? 100 : 40;
17101             var thresh = 40;
17102
17103             // How many pixels to scroll per autoscroll op.  This helps to reduce
17104             // clunky scrolling. IE is more sensitive about this ... it needs this
17105             // value to be higher.
17106             var scrAmt = (document.all) ? 80 : 30;
17107
17108             // Scroll down if we are near the bottom of the visible page and the
17109             // obj extends below the crease
17110             if ( bot > clientH && toBot < thresh ) {
17111                 window.scrollTo(sl, st + scrAmt);
17112             }
17113
17114             // Scroll up if the window is scrolled down and the top of the object
17115             // goes above the top border
17116             if ( y < st && st > 0 && y - st < thresh ) {
17117                 window.scrollTo(sl, st - scrAmt);
17118             }
17119
17120             // Scroll right if the obj is beyond the right border and the cursor is
17121             // near the border.
17122             if ( right > clientW && toRight < thresh ) {
17123                 window.scrollTo(sl + scrAmt, st);
17124             }
17125
17126             // Scroll left if the window has been scrolled to the right and the obj
17127             // extends past the left border
17128             if ( x < sl && sl > 0 && x - sl < thresh ) {
17129                 window.scrollTo(sl - scrAmt, st);
17130             }
17131         }
17132     },
17133
17134     /**
17135      * Finds the location the element should be placed if we want to move
17136      * it to where the mouse location less the click offset would place us.
17137      * @method getTargetCoord
17138      * @param {int} iPageX the X coordinate of the click
17139      * @param {int} iPageY the Y coordinate of the click
17140      * @return an object that contains the coordinates (Object.x and Object.y)
17141      * @private
17142      */
17143     getTargetCoord: function(iPageX, iPageY) {
17144
17145
17146         var x = iPageX - this.deltaX;
17147         var y = iPageY - this.deltaY;
17148
17149         if (this.constrainX) {
17150             if (x < this.minX) { x = this.minX; }
17151             if (x > this.maxX) { x = this.maxX; }
17152         }
17153
17154         if (this.constrainY) {
17155             if (y < this.minY) { y = this.minY; }
17156             if (y > this.maxY) { y = this.maxY; }
17157         }
17158
17159         x = this.getTick(x, this.xTicks);
17160         y = this.getTick(y, this.yTicks);
17161
17162
17163         return {x:x, y:y};
17164     },
17165
17166     /*
17167      * Sets up config options specific to this class. Overrides
17168      * Roo.dd.DragDrop, but all versions of this method through the
17169      * inheritance chain are called
17170      */
17171     applyConfig: function() {
17172         Roo.dd.DD.superclass.applyConfig.call(this);
17173         this.scroll = (this.config.scroll !== false);
17174     },
17175
17176     /*
17177      * Event that fires prior to the onMouseDown event.  Overrides
17178      * Roo.dd.DragDrop.
17179      */
17180     b4MouseDown: function(e) {
17181         // this.resetConstraints();
17182         this.autoOffset(e.getPageX(),
17183                             e.getPageY());
17184     },
17185
17186     /*
17187      * Event that fires prior to the onDrag event.  Overrides
17188      * Roo.dd.DragDrop.
17189      */
17190     b4Drag: function(e) {
17191         this.setDragElPos(e.getPageX(),
17192                             e.getPageY());
17193     },
17194
17195     toString: function() {
17196         return ("DD " + this.id);
17197     }
17198
17199     //////////////////////////////////////////////////////////////////////////
17200     // Debugging ygDragDrop events that can be overridden
17201     //////////////////////////////////////////////////////////////////////////
17202     /*
17203     startDrag: function(x, y) {
17204     },
17205
17206     onDrag: function(e) {
17207     },
17208
17209     onDragEnter: function(e, id) {
17210     },
17211
17212     onDragOver: function(e, id) {
17213     },
17214
17215     onDragOut: function(e, id) {
17216     },
17217
17218     onDragDrop: function(e, id) {
17219     },
17220
17221     endDrag: function(e) {
17222     }
17223
17224     */
17225
17226 });/*
17227  * Based on:
17228  * Ext JS Library 1.1.1
17229  * Copyright(c) 2006-2007, Ext JS, LLC.
17230  *
17231  * Originally Released Under LGPL - original licence link has changed is not relivant.
17232  *
17233  * Fork - LGPL
17234  * <script type="text/javascript">
17235  */
17236
17237 /**
17238  * @class Roo.dd.DDProxy
17239  * A DragDrop implementation that inserts an empty, bordered div into
17240  * the document that follows the cursor during drag operations.  At the time of
17241  * the click, the frame div is resized to the dimensions of the linked html
17242  * element, and moved to the exact location of the linked element.
17243  *
17244  * References to the "frame" element refer to the single proxy element that
17245  * was created to be dragged in place of all DDProxy elements on the
17246  * page.
17247  *
17248  * @extends Roo.dd.DD
17249  * @constructor
17250  * @param {String} id the id of the linked html element
17251  * @param {String} sGroup the group of related DragDrop objects
17252  * @param {object} config an object containing configurable attributes
17253  *                Valid properties for DDProxy in addition to those in DragDrop:
17254  *                   resizeFrame, centerFrame, dragElId
17255  */
17256 Roo.dd.DDProxy = function(id, sGroup, config) {
17257     if (id) {
17258         this.init(id, sGroup, config);
17259         this.initFrame();
17260     }
17261 };
17262
17263 /**
17264  * The default drag frame div id
17265  * @property Roo.dd.DDProxy.dragElId
17266  * @type String
17267  * @static
17268  */
17269 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17270
17271 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17272
17273     /**
17274      * By default we resize the drag frame to be the same size as the element
17275      * we want to drag (this is to get the frame effect).  We can turn it off
17276      * if we want a different behavior.
17277      * @property resizeFrame
17278      * @type boolean
17279      */
17280     resizeFrame: true,
17281
17282     /**
17283      * By default the frame is positioned exactly where the drag element is, so
17284      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17285      * you do not have constraints on the obj is to have the drag frame centered
17286      * around the cursor.  Set centerFrame to true for this effect.
17287      * @property centerFrame
17288      * @type boolean
17289      */
17290     centerFrame: false,
17291
17292     /**
17293      * Creates the proxy element if it does not yet exist
17294      * @method createFrame
17295      */
17296     createFrame: function() {
17297         var self = this;
17298         var body = document.body;
17299
17300         if (!body || !body.firstChild) {
17301             setTimeout( function() { self.createFrame(); }, 50 );
17302             return;
17303         }
17304
17305         var div = this.getDragEl();
17306
17307         if (!div) {
17308             div    = document.createElement("div");
17309             div.id = this.dragElId;
17310             var s  = div.style;
17311
17312             s.position   = "absolute";
17313             s.visibility = "hidden";
17314             s.cursor     = "move";
17315             s.border     = "2px solid #aaa";
17316             s.zIndex     = 999;
17317
17318             // appendChild can blow up IE if invoked prior to the window load event
17319             // while rendering a table.  It is possible there are other scenarios
17320             // that would cause this to happen as well.
17321             body.insertBefore(div, body.firstChild);
17322         }
17323     },
17324
17325     /**
17326      * Initialization for the drag frame element.  Must be called in the
17327      * constructor of all subclasses
17328      * @method initFrame
17329      */
17330     initFrame: function() {
17331         this.createFrame();
17332     },
17333
17334     applyConfig: function() {
17335         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17336
17337         this.resizeFrame = (this.config.resizeFrame !== false);
17338         this.centerFrame = (this.config.centerFrame);
17339         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17340     },
17341
17342     /**
17343      * Resizes the drag frame to the dimensions of the clicked object, positions
17344      * it over the object, and finally displays it
17345      * @method showFrame
17346      * @param {int} iPageX X click position
17347      * @param {int} iPageY Y click position
17348      * @private
17349      */
17350     showFrame: function(iPageX, iPageY) {
17351         var el = this.getEl();
17352         var dragEl = this.getDragEl();
17353         var s = dragEl.style;
17354
17355         this._resizeProxy();
17356
17357         if (this.centerFrame) {
17358             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17359                            Math.round(parseInt(s.height, 10)/2) );
17360         }
17361
17362         this.setDragElPos(iPageX, iPageY);
17363
17364         Roo.fly(dragEl).show();
17365     },
17366
17367     /**
17368      * The proxy is automatically resized to the dimensions of the linked
17369      * element when a drag is initiated, unless resizeFrame is set to false
17370      * @method _resizeProxy
17371      * @private
17372      */
17373     _resizeProxy: function() {
17374         if (this.resizeFrame) {
17375             var el = this.getEl();
17376             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17377         }
17378     },
17379
17380     // overrides Roo.dd.DragDrop
17381     b4MouseDown: function(e) {
17382         var x = e.getPageX();
17383         var y = e.getPageY();
17384         this.autoOffset(x, y);
17385         this.setDragElPos(x, y);
17386     },
17387
17388     // overrides Roo.dd.DragDrop
17389     b4StartDrag: function(x, y) {
17390         // show the drag frame
17391         this.showFrame(x, y);
17392     },
17393
17394     // overrides Roo.dd.DragDrop
17395     b4EndDrag: function(e) {
17396         Roo.fly(this.getDragEl()).hide();
17397     },
17398
17399     // overrides Roo.dd.DragDrop
17400     // By default we try to move the element to the last location of the frame.
17401     // This is so that the default behavior mirrors that of Roo.dd.DD.
17402     endDrag: function(e) {
17403
17404         var lel = this.getEl();
17405         var del = this.getDragEl();
17406
17407         // Show the drag frame briefly so we can get its position
17408         del.style.visibility = "";
17409
17410         this.beforeMove();
17411         // Hide the linked element before the move to get around a Safari
17412         // rendering bug.
17413         lel.style.visibility = "hidden";
17414         Roo.dd.DDM.moveToEl(lel, del);
17415         del.style.visibility = "hidden";
17416         lel.style.visibility = "";
17417
17418         this.afterDrag();
17419     },
17420
17421     beforeMove : function(){
17422
17423     },
17424
17425     afterDrag : function(){
17426
17427     },
17428
17429     toString: function() {
17430         return ("DDProxy " + this.id);
17431     }
17432
17433 });
17434 /*
17435  * Based on:
17436  * Ext JS Library 1.1.1
17437  * Copyright(c) 2006-2007, Ext JS, LLC.
17438  *
17439  * Originally Released Under LGPL - original licence link has changed is not relivant.
17440  *
17441  * Fork - LGPL
17442  * <script type="text/javascript">
17443  */
17444
17445  /**
17446  * @class Roo.dd.DDTarget
17447  * A DragDrop implementation that does not move, but can be a drop
17448  * target.  You would get the same result by simply omitting implementation
17449  * for the event callbacks, but this way we reduce the processing cost of the
17450  * event listener and the callbacks.
17451  * @extends Roo.dd.DragDrop
17452  * @constructor
17453  * @param {String} id the id of the element that is a drop target
17454  * @param {String} sGroup the group of related DragDrop objects
17455  * @param {object} config an object containing configurable attributes
17456  *                 Valid properties for DDTarget in addition to those in
17457  *                 DragDrop:
17458  *                    none
17459  */
17460 Roo.dd.DDTarget = function(id, sGroup, config) {
17461     if (id) {
17462         this.initTarget(id, sGroup, config);
17463     }
17464     if (config.listeners || config.events) { 
17465        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17466             listeners : config.listeners || {}, 
17467             events : config.events || {} 
17468         });    
17469     }
17470 };
17471
17472 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17473 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17474     toString: function() {
17475         return ("DDTarget " + this.id);
17476     }
17477 });
17478 /*
17479  * Based on:
17480  * Ext JS Library 1.1.1
17481  * Copyright(c) 2006-2007, Ext JS, LLC.
17482  *
17483  * Originally Released Under LGPL - original licence link has changed is not relivant.
17484  *
17485  * Fork - LGPL
17486  * <script type="text/javascript">
17487  */
17488  
17489
17490 /**
17491  * @class Roo.dd.ScrollManager
17492  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17493  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17494  * @singleton
17495  */
17496 Roo.dd.ScrollManager = function(){
17497     var ddm = Roo.dd.DragDropMgr;
17498     var els = {};
17499     var dragEl = null;
17500     var proc = {};
17501     
17502     var onStop = function(e){
17503         dragEl = null;
17504         clearProc();
17505     };
17506     
17507     var triggerRefresh = function(){
17508         if(ddm.dragCurrent){
17509              ddm.refreshCache(ddm.dragCurrent.groups);
17510         }
17511     };
17512     
17513     var doScroll = function(){
17514         if(ddm.dragCurrent){
17515             var dds = Roo.dd.ScrollManager;
17516             if(!dds.animate){
17517                 if(proc.el.scroll(proc.dir, dds.increment)){
17518                     triggerRefresh();
17519                 }
17520             }else{
17521                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17522             }
17523         }
17524     };
17525     
17526     var clearProc = function(){
17527         if(proc.id){
17528             clearInterval(proc.id);
17529         }
17530         proc.id = 0;
17531         proc.el = null;
17532         proc.dir = "";
17533     };
17534     
17535     var startProc = function(el, dir){
17536         clearProc();
17537         proc.el = el;
17538         proc.dir = dir;
17539         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17540     };
17541     
17542     var onFire = function(e, isDrop){
17543         if(isDrop || !ddm.dragCurrent){ return; }
17544         var dds = Roo.dd.ScrollManager;
17545         if(!dragEl || dragEl != ddm.dragCurrent){
17546             dragEl = ddm.dragCurrent;
17547             // refresh regions on drag start
17548             dds.refreshCache();
17549         }
17550         
17551         var xy = Roo.lib.Event.getXY(e);
17552         var pt = new Roo.lib.Point(xy[0], xy[1]);
17553         for(var id in els){
17554             var el = els[id], r = el._region;
17555             if(r && r.contains(pt) && el.isScrollable()){
17556                 if(r.bottom - pt.y <= dds.thresh){
17557                     if(proc.el != el){
17558                         startProc(el, "down");
17559                     }
17560                     return;
17561                 }else if(r.right - pt.x <= dds.thresh){
17562                     if(proc.el != el){
17563                         startProc(el, "left");
17564                     }
17565                     return;
17566                 }else if(pt.y - r.top <= dds.thresh){
17567                     if(proc.el != el){
17568                         startProc(el, "up");
17569                     }
17570                     return;
17571                 }else if(pt.x - r.left <= dds.thresh){
17572                     if(proc.el != el){
17573                         startProc(el, "right");
17574                     }
17575                     return;
17576                 }
17577             }
17578         }
17579         clearProc();
17580     };
17581     
17582     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17583     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17584     
17585     return {
17586         /**
17587          * Registers new overflow element(s) to auto scroll
17588          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17589          */
17590         register : function(el){
17591             if(el instanceof Array){
17592                 for(var i = 0, len = el.length; i < len; i++) {
17593                         this.register(el[i]);
17594                 }
17595             }else{
17596                 el = Roo.get(el);
17597                 els[el.id] = el;
17598             }
17599         },
17600         
17601         /**
17602          * Unregisters overflow element(s) so they are no longer scrolled
17603          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17604          */
17605         unregister : function(el){
17606             if(el instanceof Array){
17607                 for(var i = 0, len = el.length; i < len; i++) {
17608                         this.unregister(el[i]);
17609                 }
17610             }else{
17611                 el = Roo.get(el);
17612                 delete els[el.id];
17613             }
17614         },
17615         
17616         /**
17617          * The number of pixels from the edge of a container the pointer needs to be to 
17618          * trigger scrolling (defaults to 25)
17619          * @type Number
17620          */
17621         thresh : 25,
17622         
17623         /**
17624          * The number of pixels to scroll in each scroll increment (defaults to 50)
17625          * @type Number
17626          */
17627         increment : 100,
17628         
17629         /**
17630          * The frequency of scrolls in milliseconds (defaults to 500)
17631          * @type Number
17632          */
17633         frequency : 500,
17634         
17635         /**
17636          * True to animate the scroll (defaults to true)
17637          * @type Boolean
17638          */
17639         animate: true,
17640         
17641         /**
17642          * The animation duration in seconds - 
17643          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17644          * @type Number
17645          */
17646         animDuration: .4,
17647         
17648         /**
17649          * Manually trigger a cache refresh.
17650          */
17651         refreshCache : function(){
17652             for(var id in els){
17653                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17654                     els[id]._region = els[id].getRegion();
17655                 }
17656             }
17657         }
17658     };
17659 }();/*
17660  * Based on:
17661  * Ext JS Library 1.1.1
17662  * Copyright(c) 2006-2007, Ext JS, LLC.
17663  *
17664  * Originally Released Under LGPL - original licence link has changed is not relivant.
17665  *
17666  * Fork - LGPL
17667  * <script type="text/javascript">
17668  */
17669  
17670
17671 /**
17672  * @class Roo.dd.Registry
17673  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17674  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17675  * @singleton
17676  */
17677 Roo.dd.Registry = function(){
17678     var elements = {}; 
17679     var handles = {}; 
17680     var autoIdSeed = 0;
17681
17682     var getId = function(el, autogen){
17683         if(typeof el == "string"){
17684             return el;
17685         }
17686         var id = el.id;
17687         if(!id && autogen !== false){
17688             id = "roodd-" + (++autoIdSeed);
17689             el.id = id;
17690         }
17691         return id;
17692     };
17693     
17694     return {
17695     /**
17696      * Register a drag drop element
17697      * @param {String|HTMLElement} element The id or DOM node to register
17698      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17699      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17700      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17701      * populated in the data object (if applicable):
17702      * <pre>
17703 Value      Description<br />
17704 ---------  ------------------------------------------<br />
17705 handles    Array of DOM nodes that trigger dragging<br />
17706            for the element being registered<br />
17707 isHandle   True if the element passed in triggers<br />
17708            dragging itself, else false
17709 </pre>
17710      */
17711         register : function(el, data){
17712             data = data || {};
17713             if(typeof el == "string"){
17714                 el = document.getElementById(el);
17715             }
17716             data.ddel = el;
17717             elements[getId(el)] = data;
17718             if(data.isHandle !== false){
17719                 handles[data.ddel.id] = data;
17720             }
17721             if(data.handles){
17722                 var hs = data.handles;
17723                 for(var i = 0, len = hs.length; i < len; i++){
17724                         handles[getId(hs[i])] = data;
17725                 }
17726             }
17727         },
17728
17729     /**
17730      * Unregister a drag drop element
17731      * @param {String|HTMLElement}  element The id or DOM node to unregister
17732      */
17733         unregister : function(el){
17734             var id = getId(el, false);
17735             var data = elements[id];
17736             if(data){
17737                 delete elements[id];
17738                 if(data.handles){
17739                     var hs = data.handles;
17740                     for(var i = 0, len = hs.length; i < len; i++){
17741                         delete handles[getId(hs[i], false)];
17742                     }
17743                 }
17744             }
17745         },
17746
17747     /**
17748      * Returns the handle registered for a DOM Node by id
17749      * @param {String|HTMLElement} id The DOM node or id to look up
17750      * @return {Object} handle The custom handle data
17751      */
17752         getHandle : function(id){
17753             if(typeof id != "string"){ // must be element?
17754                 id = id.id;
17755             }
17756             return handles[id];
17757         },
17758
17759     /**
17760      * Returns the handle that is registered for the DOM node that is the target of the event
17761      * @param {Event} e The event
17762      * @return {Object} handle The custom handle data
17763      */
17764         getHandleFromEvent : function(e){
17765             var t = Roo.lib.Event.getTarget(e);
17766             return t ? handles[t.id] : null;
17767         },
17768
17769     /**
17770      * Returns a custom data object that is registered for a DOM node by id
17771      * @param {String|HTMLElement} id The DOM node or id to look up
17772      * @return {Object} data The custom data
17773      */
17774         getTarget : function(id){
17775             if(typeof id != "string"){ // must be element?
17776                 id = id.id;
17777             }
17778             return elements[id];
17779         },
17780
17781     /**
17782      * Returns a custom data object that is registered for the DOM node that is the target of the event
17783      * @param {Event} e The event
17784      * @return {Object} data The custom data
17785      */
17786         getTargetFromEvent : function(e){
17787             var t = Roo.lib.Event.getTarget(e);
17788             return t ? elements[t.id] || handles[t.id] : null;
17789         }
17790     };
17791 }();/*
17792  * Based on:
17793  * Ext JS Library 1.1.1
17794  * Copyright(c) 2006-2007, Ext JS, LLC.
17795  *
17796  * Originally Released Under LGPL - original licence link has changed is not relivant.
17797  *
17798  * Fork - LGPL
17799  * <script type="text/javascript">
17800  */
17801  
17802
17803 /**
17804  * @class Roo.dd.StatusProxy
17805  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17806  * default drag proxy used by all Roo.dd components.
17807  * @constructor
17808  * @param {Object} config
17809  */
17810 Roo.dd.StatusProxy = function(config){
17811     Roo.apply(this, config);
17812     this.id = this.id || Roo.id();
17813     this.el = new Roo.Layer({
17814         dh: {
17815             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17816                 {tag: "div", cls: "x-dd-drop-icon"},
17817                 {tag: "div", cls: "x-dd-drag-ghost"}
17818             ]
17819         }, 
17820         shadow: !config || config.shadow !== false
17821     });
17822     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17823     this.dropStatus = this.dropNotAllowed;
17824 };
17825
17826 Roo.dd.StatusProxy.prototype = {
17827     /**
17828      * @cfg {String} dropAllowed
17829      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17830      */
17831     dropAllowed : "x-dd-drop-ok",
17832     /**
17833      * @cfg {String} dropNotAllowed
17834      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17835      */
17836     dropNotAllowed : "x-dd-drop-nodrop",
17837
17838     /**
17839      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17840      * over the current target element.
17841      * @param {String} cssClass The css class for the new drop status indicator image
17842      */
17843     setStatus : function(cssClass){
17844         cssClass = cssClass || this.dropNotAllowed;
17845         if(this.dropStatus != cssClass){
17846             this.el.replaceClass(this.dropStatus, cssClass);
17847             this.dropStatus = cssClass;
17848         }
17849     },
17850
17851     /**
17852      * Resets the status indicator to the default dropNotAllowed value
17853      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17854      */
17855     reset : function(clearGhost){
17856         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17857         this.dropStatus = this.dropNotAllowed;
17858         if(clearGhost){
17859             this.ghost.update("");
17860         }
17861     },
17862
17863     /**
17864      * Updates the contents of the ghost element
17865      * @param {String} html The html that will replace the current innerHTML of the ghost element
17866      */
17867     update : function(html){
17868         if(typeof html == "string"){
17869             this.ghost.update(html);
17870         }else{
17871             this.ghost.update("");
17872             html.style.margin = "0";
17873             this.ghost.dom.appendChild(html);
17874         }
17875         // ensure float = none set?? cant remember why though.
17876         var el = this.ghost.dom.firstChild;
17877                 if(el){
17878                         Roo.fly(el).setStyle('float', 'none');
17879                 }
17880     },
17881     
17882     /**
17883      * Returns the underlying proxy {@link Roo.Layer}
17884      * @return {Roo.Layer} el
17885     */
17886     getEl : function(){
17887         return this.el;
17888     },
17889
17890     /**
17891      * Returns the ghost element
17892      * @return {Roo.Element} el
17893      */
17894     getGhost : function(){
17895         return this.ghost;
17896     },
17897
17898     /**
17899      * Hides the proxy
17900      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17901      */
17902     hide : function(clear){
17903         this.el.hide();
17904         if(clear){
17905             this.reset(true);
17906         }
17907     },
17908
17909     /**
17910      * Stops the repair animation if it's currently running
17911      */
17912     stop : function(){
17913         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17914             this.anim.stop();
17915         }
17916     },
17917
17918     /**
17919      * Displays this proxy
17920      */
17921     show : function(){
17922         this.el.show();
17923     },
17924
17925     /**
17926      * Force the Layer to sync its shadow and shim positions to the element
17927      */
17928     sync : function(){
17929         this.el.sync();
17930     },
17931
17932     /**
17933      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17934      * invalid drop operation by the item being dragged.
17935      * @param {Array} xy The XY position of the element ([x, y])
17936      * @param {Function} callback The function to call after the repair is complete
17937      * @param {Object} scope The scope in which to execute the callback
17938      */
17939     repair : function(xy, callback, scope){
17940         this.callback = callback;
17941         this.scope = scope;
17942         if(xy && this.animRepair !== false){
17943             this.el.addClass("x-dd-drag-repair");
17944             this.el.hideUnders(true);
17945             this.anim = this.el.shift({
17946                 duration: this.repairDuration || .5,
17947                 easing: 'easeOut',
17948                 xy: xy,
17949                 stopFx: true,
17950                 callback: this.afterRepair,
17951                 scope: this
17952             });
17953         }else{
17954             this.afterRepair();
17955         }
17956     },
17957
17958     // private
17959     afterRepair : function(){
17960         this.hide(true);
17961         if(typeof this.callback == "function"){
17962             this.callback.call(this.scope || this);
17963         }
17964         this.callback = null;
17965         this.scope = null;
17966     }
17967 };/*
17968  * Based on:
17969  * Ext JS Library 1.1.1
17970  * Copyright(c) 2006-2007, Ext JS, LLC.
17971  *
17972  * Originally Released Under LGPL - original licence link has changed is not relivant.
17973  *
17974  * Fork - LGPL
17975  * <script type="text/javascript">
17976  */
17977
17978 /**
17979  * @class Roo.dd.DragSource
17980  * @extends Roo.dd.DDProxy
17981  * A simple class that provides the basic implementation needed to make any element draggable.
17982  * @constructor
17983  * @param {String/HTMLElement/Element} el The container element
17984  * @param {Object} config
17985  */
17986 Roo.dd.DragSource = function(el, config){
17987     this.el = Roo.get(el);
17988     this.dragData = {};
17989     
17990     Roo.apply(this, config);
17991     
17992     if(!this.proxy){
17993         this.proxy = new Roo.dd.StatusProxy();
17994     }
17995
17996     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17997           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17998     
17999     this.dragging = false;
18000 };
18001
18002 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18003     /**
18004      * @cfg {String} dropAllowed
18005      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18006      */
18007     dropAllowed : "x-dd-drop-ok",
18008     /**
18009      * @cfg {String} dropNotAllowed
18010      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18011      */
18012     dropNotAllowed : "x-dd-drop-nodrop",
18013
18014     /**
18015      * Returns the data object associated with this drag source
18016      * @return {Object} data An object containing arbitrary data
18017      */
18018     getDragData : function(e){
18019         return this.dragData;
18020     },
18021
18022     // private
18023     onDragEnter : function(e, id){
18024         var target = Roo.dd.DragDropMgr.getDDById(id);
18025         this.cachedTarget = target;
18026         if(this.beforeDragEnter(target, e, id) !== false){
18027             if(target.isNotifyTarget){
18028                 var status = target.notifyEnter(this, e, this.dragData);
18029                 this.proxy.setStatus(status);
18030             }else{
18031                 this.proxy.setStatus(this.dropAllowed);
18032             }
18033             
18034             if(this.afterDragEnter){
18035                 /**
18036                  * An empty function by default, but provided so that you can perform a custom action
18037                  * when the dragged item enters the drop target by providing an implementation.
18038                  * @param {Roo.dd.DragDrop} target The drop target
18039                  * @param {Event} e The event object
18040                  * @param {String} id The id of the dragged element
18041                  * @method afterDragEnter
18042                  */
18043                 this.afterDragEnter(target, e, id);
18044             }
18045         }
18046     },
18047
18048     /**
18049      * An empty function by default, but provided so that you can perform a custom action
18050      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18051      * @param {Roo.dd.DragDrop} target The drop target
18052      * @param {Event} e The event object
18053      * @param {String} id The id of the dragged element
18054      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18055      */
18056     beforeDragEnter : function(target, e, id){
18057         return true;
18058     },
18059
18060     // private
18061     alignElWithMouse: function() {
18062         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18063         this.proxy.sync();
18064     },
18065
18066     // private
18067     onDragOver : function(e, id){
18068         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18069         if(this.beforeDragOver(target, e, id) !== false){
18070             if(target.isNotifyTarget){
18071                 var status = target.notifyOver(this, e, this.dragData);
18072                 this.proxy.setStatus(status);
18073             }
18074
18075             if(this.afterDragOver){
18076                 /**
18077                  * An empty function by default, but provided so that you can perform a custom action
18078                  * while the dragged item is over the drop target by providing an implementation.
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                  * @method afterDragOver
18083                  */
18084                 this.afterDragOver(target, e, id);
18085             }
18086         }
18087     },
18088
18089     /**
18090      * An empty function by default, but provided so that you can perform a custom action
18091      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18092      * @param {Roo.dd.DragDrop} target The drop target
18093      * @param {Event} e The event object
18094      * @param {String} id The id of the dragged element
18095      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18096      */
18097     beforeDragOver : function(target, e, id){
18098         return true;
18099     },
18100
18101     // private
18102     onDragOut : function(e, id){
18103         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18104         if(this.beforeDragOut(target, e, id) !== false){
18105             if(target.isNotifyTarget){
18106                 target.notifyOut(this, e, this.dragData);
18107             }
18108             this.proxy.reset();
18109             if(this.afterDragOut){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * after the dragged item is dragged out of the target without dropping.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOut
18117                  */
18118                 this.afterDragOut(target, e, id);
18119             }
18120         }
18121         this.cachedTarget = null;
18122     },
18123
18124     /**
18125      * An empty function by default, but provided so that you can perform a custom action before the dragged
18126      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18127      * @param {Roo.dd.DragDrop} target The drop target
18128      * @param {Event} e The event object
18129      * @param {String} id The id of the dragged element
18130      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18131      */
18132     beforeDragOut : function(target, e, id){
18133         return true;
18134     },
18135     
18136     // private
18137     onDragDrop : function(e, id){
18138         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18139         if(this.beforeDragDrop(target, e, id) !== false){
18140             if(target.isNotifyTarget){
18141                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18142                     this.onValidDrop(target, e, id);
18143                 }else{
18144                     this.onInvalidDrop(target, e, id);
18145                 }
18146             }else{
18147                 this.onValidDrop(target, e, id);
18148             }
18149             
18150             if(this.afterDragDrop){
18151                 /**
18152                  * An empty function by default, but provided so that you can perform a custom action
18153                  * after a valid drag drop has occurred by providing an implementation.
18154                  * @param {Roo.dd.DragDrop} target The drop target
18155                  * @param {Event} e The event object
18156                  * @param {String} id The id of the dropped element
18157                  * @method afterDragDrop
18158                  */
18159                 this.afterDragDrop(target, e, id);
18160             }
18161         }
18162         delete this.cachedTarget;
18163     },
18164
18165     /**
18166      * An empty function by default, but provided so that you can perform a custom action before the dragged
18167      * item is dropped onto the target and optionally cancel the onDragDrop.
18168      * @param {Roo.dd.DragDrop} target The drop target
18169      * @param {Event} e The event object
18170      * @param {String} id The id of the dragged element
18171      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18172      */
18173     beforeDragDrop : function(target, e, id){
18174         return true;
18175     },
18176
18177     // private
18178     onValidDrop : function(target, e, id){
18179         this.hideProxy();
18180         if(this.afterValidDrop){
18181             /**
18182              * An empty function by default, but provided so that you can perform a custom action
18183              * after a valid drop has occurred by providing an implementation.
18184              * @param {Object} target The target DD 
18185              * @param {Event} e The event object
18186              * @param {String} id The id of the dropped element
18187              * @method afterInvalidDrop
18188              */
18189             this.afterValidDrop(target, e, id);
18190         }
18191     },
18192
18193     // private
18194     getRepairXY : function(e, data){
18195         return this.el.getXY();  
18196     },
18197
18198     // private
18199     onInvalidDrop : function(target, e, id){
18200         this.beforeInvalidDrop(target, e, id);
18201         if(this.cachedTarget){
18202             if(this.cachedTarget.isNotifyTarget){
18203                 this.cachedTarget.notifyOut(this, e, this.dragData);
18204             }
18205             this.cacheTarget = null;
18206         }
18207         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18208
18209         if(this.afterInvalidDrop){
18210             /**
18211              * An empty function by default, but provided so that you can perform a custom action
18212              * after an invalid drop has occurred by providing an implementation.
18213              * @param {Event} e The event object
18214              * @param {String} id The id of the dropped element
18215              * @method afterInvalidDrop
18216              */
18217             this.afterInvalidDrop(e, id);
18218         }
18219     },
18220
18221     // private
18222     afterRepair : function(){
18223         if(Roo.enableFx){
18224             this.el.highlight(this.hlColor || "c3daf9");
18225         }
18226         this.dragging = false;
18227     },
18228
18229     /**
18230      * An empty function by default, but provided so that you can perform a custom action after an invalid
18231      * drop has occurred.
18232      * @param {Roo.dd.DragDrop} target The drop target
18233      * @param {Event} e The event object
18234      * @param {String} id The id of the dragged element
18235      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18236      */
18237     beforeInvalidDrop : function(target, e, id){
18238         return true;
18239     },
18240
18241     // private
18242     handleMouseDown : function(e){
18243         if(this.dragging) {
18244             return;
18245         }
18246         var data = this.getDragData(e);
18247         if(data && this.onBeforeDrag(data, e) !== false){
18248             this.dragData = data;
18249             this.proxy.stop();
18250             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18251         } 
18252     },
18253
18254     /**
18255      * An empty function by default, but provided so that you can perform a custom action before the initial
18256      * drag event begins and optionally cancel it.
18257      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18258      * @param {Event} e The event object
18259      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18260      */
18261     onBeforeDrag : function(data, e){
18262         return true;
18263     },
18264
18265     /**
18266      * An empty function by default, but provided so that you can perform a custom action once the initial
18267      * drag event has begun.  The drag cannot be canceled from this function.
18268      * @param {Number} x The x position of the click on the dragged object
18269      * @param {Number} y The y position of the click on the dragged object
18270      */
18271     onStartDrag : Roo.emptyFn,
18272
18273     // private - YUI override
18274     startDrag : function(x, y){
18275         this.proxy.reset();
18276         this.dragging = true;
18277         this.proxy.update("");
18278         this.onInitDrag(x, y);
18279         this.proxy.show();
18280     },
18281
18282     // private
18283     onInitDrag : function(x, y){
18284         var clone = this.el.dom.cloneNode(true);
18285         clone.id = Roo.id(); // prevent duplicate ids
18286         this.proxy.update(clone);
18287         this.onStartDrag(x, y);
18288         return true;
18289     },
18290
18291     /**
18292      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18293      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18294      */
18295     getProxy : function(){
18296         return this.proxy;  
18297     },
18298
18299     /**
18300      * Hides the drag source's {@link Roo.dd.StatusProxy}
18301      */
18302     hideProxy : function(){
18303         this.proxy.hide();  
18304         this.proxy.reset(true);
18305         this.dragging = false;
18306     },
18307
18308     // private
18309     triggerCacheRefresh : function(){
18310         Roo.dd.DDM.refreshCache(this.groups);
18311     },
18312
18313     // private - override to prevent hiding
18314     b4EndDrag: function(e) {
18315     },
18316
18317     // private - override to prevent moving
18318     endDrag : function(e){
18319         this.onEndDrag(this.dragData, e);
18320     },
18321
18322     // private
18323     onEndDrag : function(data, e){
18324     },
18325     
18326     // private - pin to cursor
18327     autoOffset : function(x, y) {
18328         this.setDelta(-12, -20);
18329     }    
18330 });/*
18331  * Based on:
18332  * Ext JS Library 1.1.1
18333  * Copyright(c) 2006-2007, Ext JS, LLC.
18334  *
18335  * Originally Released Under LGPL - original licence link has changed is not relivant.
18336  *
18337  * Fork - LGPL
18338  * <script type="text/javascript">
18339  */
18340
18341
18342 /**
18343  * @class Roo.dd.DropTarget
18344  * @extends Roo.dd.DDTarget
18345  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18346  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18347  * @constructor
18348  * @param {String/HTMLElement/Element} el The container element
18349  * @param {Object} config
18350  */
18351 Roo.dd.DropTarget = function(el, config){
18352     this.el = Roo.get(el);
18353     
18354     var listeners = false; ;
18355     if (config && config.listeners) {
18356         listeners= config.listeners;
18357         delete config.listeners;
18358     }
18359     Roo.apply(this, config);
18360     
18361     if(this.containerScroll){
18362         Roo.dd.ScrollManager.register(this.el);
18363     }
18364     this.addEvents( {
18365          /**
18366          * @scope Roo.dd.DropTarget
18367          */
18368          
18369          /**
18370          * @event enter
18371          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18372          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18373          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18374          * 
18375          * IMPORTANT : it should set this.overClass and this.dropAllowed
18376          * 
18377          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18378          * @param {Event} e The event
18379          * @param {Object} data An object containing arbitrary data supplied by the drag source
18380          */
18381         "enter" : true,
18382         
18383          /**
18384          * @event over
18385          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18386          * This method will be called on every mouse movement while the drag source is over the drop target.
18387          * This default implementation simply returns the dropAllowed config value.
18388          * 
18389          * IMPORTANT : it should set this.dropAllowed
18390          * 
18391          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18392          * @param {Event} e The event
18393          * @param {Object} data An object containing arbitrary data supplied by the drag source
18394          
18395          */
18396         "over" : true,
18397         /**
18398          * @event out
18399          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18400          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18401          * overClass (if any) from the drop element.
18402          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18403          * @param {Event} e The event
18404          * @param {Object} data An object containing arbitrary data supplied by the drag source
18405          */
18406          "out" : true,
18407          
18408         /**
18409          * @event drop
18410          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18411          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18412          * implementation that does something to process the drop event and returns true so that the drag source's
18413          * repair action does not run.
18414          * 
18415          * IMPORTANT : it should set this.success
18416          * 
18417          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18418          * @param {Event} e The event
18419          * @param {Object} data An object containing arbitrary data supplied by the drag source
18420         */
18421          "drop" : true
18422     });
18423             
18424      
18425     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18426         this.el.dom, 
18427         this.ddGroup || this.group,
18428         {
18429             isTarget: true,
18430             listeners : listeners || {} 
18431            
18432         
18433         }
18434     );
18435
18436 };
18437
18438 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18439     /**
18440      * @cfg {String} overClass
18441      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18442      */
18443      /**
18444      * @cfg {String} ddGroup
18445      * The drag drop group to handle drop events for
18446      */
18447      
18448     /**
18449      * @cfg {String} dropAllowed
18450      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18451      */
18452     dropAllowed : "x-dd-drop-ok",
18453     /**
18454      * @cfg {String} dropNotAllowed
18455      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18456      */
18457     dropNotAllowed : "x-dd-drop-nodrop",
18458     /**
18459      * @cfg {boolean} success
18460      * set this after drop listener.. 
18461      */
18462     success : false,
18463     /**
18464      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18465      * if the drop point is valid for over/enter..
18466      */
18467     valid : false,
18468     // private
18469     isTarget : true,
18470
18471     // private
18472     isNotifyTarget : true,
18473     
18474     /**
18475      * @hide
18476      */
18477     notifyEnter : function(dd, e, data)
18478     {
18479         this.valid = true;
18480         this.fireEvent('enter', dd, e, data);
18481         if(this.overClass){
18482             this.el.addClass(this.overClass);
18483         }
18484         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18485             this.valid ? this.dropAllowed : this.dropNotAllowed
18486         );
18487     },
18488
18489     /**
18490      * @hide
18491      */
18492     notifyOver : function(dd, e, data)
18493     {
18494         this.valid = true;
18495         this.fireEvent('over', dd, e, data);
18496         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18497             this.valid ? this.dropAllowed : this.dropNotAllowed
18498         );
18499     },
18500
18501     /**
18502      * @hide
18503      */
18504     notifyOut : function(dd, e, data)
18505     {
18506         this.fireEvent('out', dd, e, data);
18507         if(this.overClass){
18508             this.el.removeClass(this.overClass);
18509         }
18510     },
18511
18512     /**
18513      * @hide
18514      */
18515     notifyDrop : function(dd, e, data)
18516     {
18517         this.success = false;
18518         this.fireEvent('drop', dd, e, data);
18519         return this.success;
18520     }
18521 });/*
18522  * Based on:
18523  * Ext JS Library 1.1.1
18524  * Copyright(c) 2006-2007, Ext JS, LLC.
18525  *
18526  * Originally Released Under LGPL - original licence link has changed is not relivant.
18527  *
18528  * Fork - LGPL
18529  * <script type="text/javascript">
18530  */
18531
18532
18533 /**
18534  * @class Roo.dd.DragZone
18535  * @extends Roo.dd.DragSource
18536  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18537  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18538  * @constructor
18539  * @param {String/HTMLElement/Element} el The container element
18540  * @param {Object} config
18541  */
18542 Roo.dd.DragZone = function(el, config){
18543     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18544     if(this.containerScroll){
18545         Roo.dd.ScrollManager.register(this.el);
18546     }
18547 };
18548
18549 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18550     /**
18551      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18552      * for auto scrolling during drag operations.
18553      */
18554     /**
18555      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18556      * method after a failed drop (defaults to "c3daf9" - light blue)
18557      */
18558
18559     /**
18560      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18561      * for a valid target to drag based on the mouse down. Override this method
18562      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18563      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18564      * @param {EventObject} e The mouse down event
18565      * @return {Object} The dragData
18566      */
18567     getDragData : function(e){
18568         return Roo.dd.Registry.getHandleFromEvent(e);
18569     },
18570     
18571     /**
18572      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18573      * this.dragData.ddel
18574      * @param {Number} x The x position of the click on the dragged object
18575      * @param {Number} y The y position of the click on the dragged object
18576      * @return {Boolean} true to continue the drag, false to cancel
18577      */
18578     onInitDrag : function(x, y){
18579         this.proxy.update(this.dragData.ddel.cloneNode(true));
18580         this.onStartDrag(x, y);
18581         return true;
18582     },
18583     
18584     /**
18585      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18586      */
18587     afterRepair : function(){
18588         if(Roo.enableFx){
18589             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18590         }
18591         this.dragging = false;
18592     },
18593
18594     /**
18595      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18596      * the XY of this.dragData.ddel
18597      * @param {EventObject} e The mouse up event
18598      * @return {Array} The xy location (e.g. [100, 200])
18599      */
18600     getRepairXY : function(e){
18601         return Roo.Element.fly(this.dragData.ddel).getXY();  
18602     }
18603 });/*
18604  * Based on:
18605  * Ext JS Library 1.1.1
18606  * Copyright(c) 2006-2007, Ext JS, LLC.
18607  *
18608  * Originally Released Under LGPL - original licence link has changed is not relivant.
18609  *
18610  * Fork - LGPL
18611  * <script type="text/javascript">
18612  */
18613 /**
18614  * @class Roo.dd.DropZone
18615  * @extends Roo.dd.DropTarget
18616  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18617  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18618  * @constructor
18619  * @param {String/HTMLElement/Element} el The container element
18620  * @param {Object} config
18621  */
18622 Roo.dd.DropZone = function(el, config){
18623     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18624 };
18625
18626 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18627     /**
18628      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18629      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18630      * provide your own custom lookup.
18631      * @param {Event} e The event
18632      * @return {Object} data The custom data
18633      */
18634     getTargetFromEvent : function(e){
18635         return Roo.dd.Registry.getTargetFromEvent(e);
18636     },
18637
18638     /**
18639      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18640      * that it has registered.  This method has no default implementation and should be overridden to provide
18641      * node-specific processing if necessary.
18642      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18643      * {@link #getTargetFromEvent} for this node)
18644      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18645      * @param {Event} e The event
18646      * @param {Object} data An object containing arbitrary data supplied by the drag source
18647      */
18648     onNodeEnter : function(n, dd, e, data){
18649         
18650     },
18651
18652     /**
18653      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18654      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18655      * overridden to provide the proper feedback.
18656      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18657      * {@link #getTargetFromEvent} for this node)
18658      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18659      * @param {Event} e The event
18660      * @param {Object} data An object containing arbitrary data supplied by the drag source
18661      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18662      * underlying {@link Roo.dd.StatusProxy} can be updated
18663      */
18664     onNodeOver : function(n, dd, e, data){
18665         return this.dropAllowed;
18666     },
18667
18668     /**
18669      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18670      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18671      * node-specific processing if necessary.
18672      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18673      * {@link #getTargetFromEvent} for this node)
18674      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18675      * @param {Event} e The event
18676      * @param {Object} data An object containing arbitrary data supplied by the drag source
18677      */
18678     onNodeOut : function(n, dd, e, data){
18679         
18680     },
18681
18682     /**
18683      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18684      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18685      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18686      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18687      * {@link #getTargetFromEvent} for this node)
18688      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18689      * @param {Event} e The event
18690      * @param {Object} data An object containing arbitrary data supplied by the drag source
18691      * @return {Boolean} True if the drop was valid, else false
18692      */
18693     onNodeDrop : function(n, dd, e, data){
18694         return false;
18695     },
18696
18697     /**
18698      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18699      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18700      * it should be overridden to provide the proper feedback if necessary.
18701      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18702      * @param {Event} e The event
18703      * @param {Object} data An object containing arbitrary data supplied by the drag source
18704      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18705      * underlying {@link Roo.dd.StatusProxy} can be updated
18706      */
18707     onContainerOver : function(dd, e, data){
18708         return this.dropNotAllowed;
18709     },
18710
18711     /**
18712      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18713      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18714      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18715      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
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     onContainerDrop : function(dd, e, data){
18722         return false;
18723     },
18724
18725     /**
18726      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18727      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18728      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18729      * you should override this method and provide a custom implementation.
18730      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18731      * @param {Event} e The event
18732      * @param {Object} data An object containing arbitrary data supplied by the drag source
18733      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18734      * underlying {@link Roo.dd.StatusProxy} can be updated
18735      */
18736     notifyEnter : function(dd, e, data){
18737         return this.dropNotAllowed;
18738     },
18739
18740     /**
18741      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18742      * This method will be called on every mouse movement while the drag source is over the drop zone.
18743      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18744      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18745      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18746      * registered node, it will call {@link #onContainerOver}.
18747      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18748      * @param {Event} e The event
18749      * @param {Object} data An object containing arbitrary data supplied by the drag source
18750      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18751      * underlying {@link Roo.dd.StatusProxy} can be updated
18752      */
18753     notifyOver : function(dd, e, data){
18754         var n = this.getTargetFromEvent(e);
18755         if(!n){ // not over valid drop target
18756             if(this.lastOverNode){
18757                 this.onNodeOut(this.lastOverNode, dd, e, data);
18758                 this.lastOverNode = null;
18759             }
18760             return this.onContainerOver(dd, e, data);
18761         }
18762         if(this.lastOverNode != n){
18763             if(this.lastOverNode){
18764                 this.onNodeOut(this.lastOverNode, dd, e, data);
18765             }
18766             this.onNodeEnter(n, dd, e, data);
18767             this.lastOverNode = n;
18768         }
18769         return this.onNodeOver(n, dd, e, data);
18770     },
18771
18772     /**
18773      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18774      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18775      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18776      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18777      * @param {Event} e The event
18778      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18779      */
18780     notifyOut : function(dd, e, data){
18781         if(this.lastOverNode){
18782             this.onNodeOut(this.lastOverNode, dd, e, data);
18783             this.lastOverNode = null;
18784         }
18785     },
18786
18787     /**
18788      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18789      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18790      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18791      * otherwise it will call {@link #onContainerDrop}.
18792      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18793      * @param {Event} e The event
18794      * @param {Object} data An object containing arbitrary data supplied by the drag source
18795      * @return {Boolean} True if the drop was valid, else false
18796      */
18797     notifyDrop : function(dd, e, data){
18798         if(this.lastOverNode){
18799             this.onNodeOut(this.lastOverNode, dd, e, data);
18800             this.lastOverNode = null;
18801         }
18802         var n = this.getTargetFromEvent(e);
18803         return n ?
18804             this.onNodeDrop(n, dd, e, data) :
18805             this.onContainerDrop(dd, e, data);
18806     },
18807
18808     // private
18809     triggerCacheRefresh : function(){
18810         Roo.dd.DDM.refreshCache(this.groups);
18811     }  
18812 });/*
18813  * Based on:
18814  * Ext JS Library 1.1.1
18815  * Copyright(c) 2006-2007, Ext JS, LLC.
18816  *
18817  * Originally Released Under LGPL - original licence link has changed is not relivant.
18818  *
18819  * Fork - LGPL
18820  * <script type="text/javascript">
18821  */
18822
18823
18824 /**
18825  * @class Roo.data.SortTypes
18826  * @singleton
18827  * Defines the default sorting (casting?) comparison functions used when sorting data.
18828  */
18829 Roo.data.SortTypes = {
18830     /**
18831      * Default sort that does nothing
18832      * @param {Mixed} s The value being converted
18833      * @return {Mixed} The comparison value
18834      */
18835     none : function(s){
18836         return s;
18837     },
18838     
18839     /**
18840      * The regular expression used to strip tags
18841      * @type {RegExp}
18842      * @property
18843      */
18844     stripTagsRE : /<\/?[^>]+>/gi,
18845     
18846     /**
18847      * Strips all HTML tags to sort on text only
18848      * @param {Mixed} s The value being converted
18849      * @return {String} The comparison value
18850      */
18851     asText : function(s){
18852         return String(s).replace(this.stripTagsRE, "");
18853     },
18854     
18855     /**
18856      * Strips all HTML tags to sort on text only - Case insensitive
18857      * @param {Mixed} s The value being converted
18858      * @return {String} The comparison value
18859      */
18860     asUCText : function(s){
18861         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18862     },
18863     
18864     /**
18865      * Case insensitive string
18866      * @param {Mixed} s The value being converted
18867      * @return {String} The comparison value
18868      */
18869     asUCString : function(s) {
18870         return String(s).toUpperCase();
18871     },
18872     
18873     /**
18874      * Date sorting
18875      * @param {Mixed} s The value being converted
18876      * @return {Number} The comparison value
18877      */
18878     asDate : function(s) {
18879         if(!s){
18880             return 0;
18881         }
18882         if(s instanceof Date){
18883             return s.getTime();
18884         }
18885         return Date.parse(String(s));
18886     },
18887     
18888     /**
18889      * Float sorting
18890      * @param {Mixed} s The value being converted
18891      * @return {Float} The comparison value
18892      */
18893     asFloat : function(s) {
18894         var val = parseFloat(String(s).replace(/,/g, ""));
18895         if(isNaN(val)) val = 0;
18896         return val;
18897     },
18898     
18899     /**
18900      * Integer sorting
18901      * @param {Mixed} s The value being converted
18902      * @return {Number} The comparison value
18903      */
18904     asInt : function(s) {
18905         var val = parseInt(String(s).replace(/,/g, ""));
18906         if(isNaN(val)) val = 0;
18907         return val;
18908     }
18909 };/*
18910  * Based on:
18911  * Ext JS Library 1.1.1
18912  * Copyright(c) 2006-2007, Ext JS, LLC.
18913  *
18914  * Originally Released Under LGPL - original licence link has changed is not relivant.
18915  *
18916  * Fork - LGPL
18917  * <script type="text/javascript">
18918  */
18919
18920 /**
18921 * @class Roo.data.Record
18922  * Instances of this class encapsulate both record <em>definition</em> information, and record
18923  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18924  * to access Records cached in an {@link Roo.data.Store} object.<br>
18925  * <p>
18926  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18927  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18928  * objects.<br>
18929  * <p>
18930  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18931  * @constructor
18932  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18933  * {@link #create}. The parameters are the same.
18934  * @param {Array} data An associative Array of data values keyed by the field name.
18935  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18936  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18937  * not specified an integer id is generated.
18938  */
18939 Roo.data.Record = function(data, id){
18940     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18941     this.data = data;
18942 };
18943
18944 /**
18945  * Generate a constructor for a specific record layout.
18946  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18947  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18948  * Each field definition object may contain the following properties: <ul>
18949  * <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,
18950  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18951  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18952  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18953  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18954  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18955  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18956  * this may be omitted.</p></li>
18957  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18958  * <ul><li>auto (Default, implies no conversion)</li>
18959  * <li>string</li>
18960  * <li>int</li>
18961  * <li>float</li>
18962  * <li>boolean</li>
18963  * <li>date</li></ul></p></li>
18964  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18965  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18966  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18967  * by the Reader into an object that will be stored in the Record. It is passed the
18968  * following parameters:<ul>
18969  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18970  * </ul></p></li>
18971  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18972  * </ul>
18973  * <br>usage:<br><pre><code>
18974 var TopicRecord = Roo.data.Record.create(
18975     {name: 'title', mapping: 'topic_title'},
18976     {name: 'author', mapping: 'username'},
18977     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18978     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18979     {name: 'lastPoster', mapping: 'user2'},
18980     {name: 'excerpt', mapping: 'post_text'}
18981 );
18982
18983 var myNewRecord = new TopicRecord({
18984     title: 'Do my job please',
18985     author: 'noobie',
18986     totalPosts: 1,
18987     lastPost: new Date(),
18988     lastPoster: 'Animal',
18989     excerpt: 'No way dude!'
18990 });
18991 myStore.add(myNewRecord);
18992 </code></pre>
18993  * @method create
18994  * @static
18995  */
18996 Roo.data.Record.create = function(o){
18997     var f = function(){
18998         f.superclass.constructor.apply(this, arguments);
18999     };
19000     Roo.extend(f, Roo.data.Record);
19001     var p = f.prototype;
19002     p.fields = new Roo.util.MixedCollection(false, function(field){
19003         return field.name;
19004     });
19005     for(var i = 0, len = o.length; i < len; i++){
19006         p.fields.add(new Roo.data.Field(o[i]));
19007     }
19008     f.getField = function(name){
19009         return p.fields.get(name);  
19010     };
19011     return f;
19012 };
19013
19014 Roo.data.Record.AUTO_ID = 1000;
19015 Roo.data.Record.EDIT = 'edit';
19016 Roo.data.Record.REJECT = 'reject';
19017 Roo.data.Record.COMMIT = 'commit';
19018
19019 Roo.data.Record.prototype = {
19020     /**
19021      * Readonly flag - true if this record has been modified.
19022      * @type Boolean
19023      */
19024     dirty : false,
19025     editing : false,
19026     error: null,
19027     modified: null,
19028
19029     // private
19030     join : function(store){
19031         this.store = store;
19032     },
19033
19034     /**
19035      * Set the named field to the specified value.
19036      * @param {String} name The name of the field to set.
19037      * @param {Object} value The value to set the field to.
19038      */
19039     set : function(name, value){
19040         if(this.data[name] == value){
19041             return;
19042         }
19043         this.dirty = true;
19044         if(!this.modified){
19045             this.modified = {};
19046         }
19047         if(typeof this.modified[name] == 'undefined'){
19048             this.modified[name] = this.data[name];
19049         }
19050         this.data[name] = value;
19051         if(!this.editing && this.store){
19052             this.store.afterEdit(this);
19053         }       
19054     },
19055
19056     /**
19057      * Get the value of the named field.
19058      * @param {String} name The name of the field to get the value of.
19059      * @return {Object} The value of the field.
19060      */
19061     get : function(name){
19062         return this.data[name]; 
19063     },
19064
19065     // private
19066     beginEdit : function(){
19067         this.editing = true;
19068         this.modified = {}; 
19069     },
19070
19071     // private
19072     cancelEdit : function(){
19073         this.editing = false;
19074         delete this.modified;
19075     },
19076
19077     // private
19078     endEdit : function(){
19079         this.editing = false;
19080         if(this.dirty && this.store){
19081             this.store.afterEdit(this);
19082         }
19083     },
19084
19085     /**
19086      * Usually called by the {@link Roo.data.Store} which owns the Record.
19087      * Rejects all changes made to the Record since either creation, or the last commit operation.
19088      * Modified fields are reverted to their original values.
19089      * <p>
19090      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19091      * of reject operations.
19092      */
19093     reject : function(){
19094         var m = this.modified;
19095         for(var n in m){
19096             if(typeof m[n] != "function"){
19097                 this.data[n] = m[n];
19098             }
19099         }
19100         this.dirty = false;
19101         delete this.modified;
19102         this.editing = false;
19103         if(this.store){
19104             this.store.afterReject(this);
19105         }
19106     },
19107
19108     /**
19109      * Usually called by the {@link Roo.data.Store} which owns the Record.
19110      * Commits all changes made to the Record since either creation, or the last commit operation.
19111      * <p>
19112      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19113      * of commit operations.
19114      */
19115     commit : function(){
19116         this.dirty = false;
19117         delete this.modified;
19118         this.editing = false;
19119         if(this.store){
19120             this.store.afterCommit(this);
19121         }
19122     },
19123
19124     // private
19125     hasError : function(){
19126         return this.error != null;
19127     },
19128
19129     // private
19130     clearError : function(){
19131         this.error = null;
19132     },
19133
19134     /**
19135      * Creates a copy of this record.
19136      * @param {String} id (optional) A new record id if you don't want to use this record's id
19137      * @return {Record}
19138      */
19139     copy : function(newId) {
19140         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19141     }
19142 };/*
19143  * Based on:
19144  * Ext JS Library 1.1.1
19145  * Copyright(c) 2006-2007, Ext JS, LLC.
19146  *
19147  * Originally Released Under LGPL - original licence link has changed is not relivant.
19148  *
19149  * Fork - LGPL
19150  * <script type="text/javascript">
19151  */
19152
19153
19154
19155 /**
19156  * @class Roo.data.Store
19157  * @extends Roo.util.Observable
19158  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19159  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19160  * <p>
19161  * 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
19162  * has no knowledge of the format of the data returned by the Proxy.<br>
19163  * <p>
19164  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19165  * instances from the data object. These records are cached and made available through accessor functions.
19166  * @constructor
19167  * Creates a new Store.
19168  * @param {Object} config A config object containing the objects needed for the Store to access data,
19169  * and read the data into Records.
19170  */
19171 Roo.data.Store = function(config){
19172     this.data = new Roo.util.MixedCollection(false);
19173     this.data.getKey = function(o){
19174         return o.id;
19175     };
19176     this.baseParams = {};
19177     // private
19178     this.paramNames = {
19179         "start" : "start",
19180         "limit" : "limit",
19181         "sort" : "sort",
19182         "dir" : "dir",
19183         "multisort" : "_multisort"
19184     };
19185
19186     if(config && config.data){
19187         this.inlineData = config.data;
19188         delete config.data;
19189     }
19190
19191     Roo.apply(this, config);
19192     
19193     if(this.reader){ // reader passed
19194         this.reader = Roo.factory(this.reader, Roo.data);
19195         this.reader.xmodule = this.xmodule || false;
19196         if(!this.recordType){
19197             this.recordType = this.reader.recordType;
19198         }
19199         if(this.reader.onMetaChange){
19200             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19201         }
19202     }
19203
19204     if(this.recordType){
19205         this.fields = this.recordType.prototype.fields;
19206     }
19207     this.modified = [];
19208
19209     this.addEvents({
19210         /**
19211          * @event datachanged
19212          * Fires when the data cache has changed, and a widget which is using this Store
19213          * as a Record cache should refresh its view.
19214          * @param {Store} this
19215          */
19216         datachanged : true,
19217         /**
19218          * @event metachange
19219          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19220          * @param {Store} this
19221          * @param {Object} meta The JSON metadata
19222          */
19223         metachange : true,
19224         /**
19225          * @event add
19226          * Fires when Records have been added to the Store
19227          * @param {Store} this
19228          * @param {Roo.data.Record[]} records The array of Records added
19229          * @param {Number} index The index at which the record(s) were added
19230          */
19231         add : true,
19232         /**
19233          * @event remove
19234          * Fires when a Record has been removed from the Store
19235          * @param {Store} this
19236          * @param {Roo.data.Record} record The Record that was removed
19237          * @param {Number} index The index at which the record was removed
19238          */
19239         remove : true,
19240         /**
19241          * @event update
19242          * Fires when a Record has been updated
19243          * @param {Store} this
19244          * @param {Roo.data.Record} record The Record that was updated
19245          * @param {String} operation The update operation being performed.  Value may be one of:
19246          * <pre><code>
19247  Roo.data.Record.EDIT
19248  Roo.data.Record.REJECT
19249  Roo.data.Record.COMMIT
19250          * </code></pre>
19251          */
19252         update : true,
19253         /**
19254          * @event clear
19255          * Fires when the data cache has been cleared.
19256          * @param {Store} this
19257          */
19258         clear : true,
19259         /**
19260          * @event beforeload
19261          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19262          * the load action will be canceled.
19263          * @param {Store} this
19264          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19265          */
19266         beforeload : true,
19267         /**
19268          * @event load
19269          * Fires after a new set of Records has been loaded.
19270          * @param {Store} this
19271          * @param {Roo.data.Record[]} records The Records that were loaded
19272          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19273          */
19274         load : true,
19275         /**
19276          * @event loadexception
19277          * Fires if an exception occurs in the Proxy during loading.
19278          * Called with the signature of the Proxy's "loadexception" event.
19279          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19280          * 
19281          * @param {Proxy} 
19282          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19283          * @param {Object} load options 
19284          * @param {Object} jsonData from your request (normally this contains the Exception)
19285          */
19286         loadexception : true
19287     });
19288     
19289     if(this.proxy){
19290         this.proxy = Roo.factory(this.proxy, Roo.data);
19291         this.proxy.xmodule = this.xmodule || false;
19292         this.relayEvents(this.proxy,  ["loadexception"]);
19293     }
19294     this.sortToggle = {};
19295     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19296
19297     Roo.data.Store.superclass.constructor.call(this);
19298
19299     if(this.inlineData){
19300         this.loadData(this.inlineData);
19301         delete this.inlineData;
19302     }
19303 };
19304 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19305      /**
19306     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19307     * without a remote query - used by combo/forms at present.
19308     */
19309     
19310     /**
19311     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19312     */
19313     /**
19314     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19315     */
19316     /**
19317     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19318     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19319     */
19320     /**
19321     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19322     * on any HTTP request
19323     */
19324     /**
19325     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19326     */
19327     /**
19328     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19329     */
19330     multiSort: false,
19331     /**
19332     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19333     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19334     */
19335     remoteSort : false,
19336
19337     /**
19338     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19339      * loaded or when a record is removed. (defaults to false).
19340     */
19341     pruneModifiedRecords : false,
19342
19343     // private
19344     lastOptions : null,
19345
19346     /**
19347      * Add Records to the Store and fires the add event.
19348      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19349      */
19350     add : function(records){
19351         records = [].concat(records);
19352         for(var i = 0, len = records.length; i < len; i++){
19353             records[i].join(this);
19354         }
19355         var index = this.data.length;
19356         this.data.addAll(records);
19357         this.fireEvent("add", this, records, index);
19358     },
19359
19360     /**
19361      * Remove a Record from the Store and fires the remove event.
19362      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19363      */
19364     remove : function(record){
19365         var index = this.data.indexOf(record);
19366         this.data.removeAt(index);
19367         if(this.pruneModifiedRecords){
19368             this.modified.remove(record);
19369         }
19370         this.fireEvent("remove", this, record, index);
19371     },
19372
19373     /**
19374      * Remove all Records from the Store and fires the clear event.
19375      */
19376     removeAll : function(){
19377         this.data.clear();
19378         if(this.pruneModifiedRecords){
19379             this.modified = [];
19380         }
19381         this.fireEvent("clear", this);
19382     },
19383
19384     /**
19385      * Inserts Records to the Store at the given index and fires the add event.
19386      * @param {Number} index The start index at which to insert the passed Records.
19387      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19388      */
19389     insert : function(index, records){
19390         records = [].concat(records);
19391         for(var i = 0, len = records.length; i < len; i++){
19392             this.data.insert(index, records[i]);
19393             records[i].join(this);
19394         }
19395         this.fireEvent("add", this, records, index);
19396     },
19397
19398     /**
19399      * Get the index within the cache of the passed Record.
19400      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19401      * @return {Number} The index of the passed Record. Returns -1 if not found.
19402      */
19403     indexOf : function(record){
19404         return this.data.indexOf(record);
19405     },
19406
19407     /**
19408      * Get the index within the cache of the Record with the passed id.
19409      * @param {String} id The id of the Record to find.
19410      * @return {Number} The index of the Record. Returns -1 if not found.
19411      */
19412     indexOfId : function(id){
19413         return this.data.indexOfKey(id);
19414     },
19415
19416     /**
19417      * Get the Record with the specified id.
19418      * @param {String} id The id of the Record to find.
19419      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19420      */
19421     getById : function(id){
19422         return this.data.key(id);
19423     },
19424
19425     /**
19426      * Get the Record at the specified index.
19427      * @param {Number} index The index of the Record to find.
19428      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19429      */
19430     getAt : function(index){
19431         return this.data.itemAt(index);
19432     },
19433
19434     /**
19435      * Returns a range of Records between specified indices.
19436      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19437      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19438      * @return {Roo.data.Record[]} An array of Records
19439      */
19440     getRange : function(start, end){
19441         return this.data.getRange(start, end);
19442     },
19443
19444     // private
19445     storeOptions : function(o){
19446         o = Roo.apply({}, o);
19447         delete o.callback;
19448         delete o.scope;
19449         this.lastOptions = o;
19450     },
19451
19452     /**
19453      * Loads the Record cache from the configured Proxy using the configured Reader.
19454      * <p>
19455      * If using remote paging, then the first load call must specify the <em>start</em>
19456      * and <em>limit</em> properties in the options.params property to establish the initial
19457      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19458      * <p>
19459      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19460      * and this call will return before the new data has been loaded. Perform any post-processing
19461      * in a callback function, or in a "load" event handler.</strong>
19462      * <p>
19463      * @param {Object} options An object containing properties which control loading options:<ul>
19464      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19465      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19466      * passed the following arguments:<ul>
19467      * <li>r : Roo.data.Record[]</li>
19468      * <li>options: Options object from the load call</li>
19469      * <li>success: Boolean success indicator</li></ul></li>
19470      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19471      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19472      * </ul>
19473      */
19474     load : function(options){
19475         options = options || {};
19476         if(this.fireEvent("beforeload", this, options) !== false){
19477             this.storeOptions(options);
19478             var p = Roo.apply(options.params || {}, this.baseParams);
19479             // if meta was not loaded from remote source.. try requesting it.
19480             if (!this.reader.metaFromRemote) {
19481                 p._requestMeta = 1;
19482             }
19483             if(this.sortInfo && this.remoteSort){
19484                 var pn = this.paramNames;
19485                 p[pn["sort"]] = this.sortInfo.field;
19486                 p[pn["dir"]] = this.sortInfo.direction;
19487             }
19488             if (this.multiSort) {
19489                 var pn = this.paramNames;
19490                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19491             }
19492             
19493             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19494         }
19495     },
19496
19497     /**
19498      * Reloads the Record cache from the configured Proxy using the configured Reader and
19499      * the options from the last load operation performed.
19500      * @param {Object} options (optional) An object containing properties which may override the options
19501      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19502      * the most recently used options are reused).
19503      */
19504     reload : function(options){
19505         this.load(Roo.applyIf(options||{}, this.lastOptions));
19506     },
19507
19508     // private
19509     // Called as a callback by the Reader during a load operation.
19510     loadRecords : function(o, options, success){
19511         if(!o || success === false){
19512             if(success !== false){
19513                 this.fireEvent("load", this, [], options);
19514             }
19515             if(options.callback){
19516                 options.callback.call(options.scope || this, [], options, false);
19517             }
19518             return;
19519         }
19520         // if data returned failure - throw an exception.
19521         if (o.success === false) {
19522             // show a message if no listener is registered.
19523             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19524                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19525             }
19526             // loadmask wil be hooked into this..
19527             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19528             return;
19529         }
19530         var r = o.records, t = o.totalRecords || r.length;
19531         if(!options || options.add !== true){
19532             if(this.pruneModifiedRecords){
19533                 this.modified = [];
19534             }
19535             for(var i = 0, len = r.length; i < len; i++){
19536                 r[i].join(this);
19537             }
19538             if(this.snapshot){
19539                 this.data = this.snapshot;
19540                 delete this.snapshot;
19541             }
19542             this.data.clear();
19543             this.data.addAll(r);
19544             this.totalLength = t;
19545             this.applySort();
19546             this.fireEvent("datachanged", this);
19547         }else{
19548             this.totalLength = Math.max(t, this.data.length+r.length);
19549             this.add(r);
19550         }
19551         this.fireEvent("load", this, r, options);
19552         if(options.callback){
19553             options.callback.call(options.scope || this, r, options, true);
19554         }
19555     },
19556
19557
19558     /**
19559      * Loads data from a passed data block. A Reader which understands the format of the data
19560      * must have been configured in the constructor.
19561      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19562      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19563      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19564      */
19565     loadData : function(o, append){
19566         var r = this.reader.readRecords(o);
19567         this.loadRecords(r, {add: append}, true);
19568     },
19569
19570     /**
19571      * Gets the number of cached records.
19572      * <p>
19573      * <em>If using paging, this may not be the total size of the dataset. If the data object
19574      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19575      * the data set size</em>
19576      */
19577     getCount : function(){
19578         return this.data.length || 0;
19579     },
19580
19581     /**
19582      * Gets the total number of records in the dataset as returned by the server.
19583      * <p>
19584      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19585      * the dataset size</em>
19586      */
19587     getTotalCount : function(){
19588         return this.totalLength || 0;
19589     },
19590
19591     /**
19592      * Returns the sort state of the Store as an object with two properties:
19593      * <pre><code>
19594  field {String} The name of the field by which the Records are sorted
19595  direction {String} The sort order, "ASC" or "DESC"
19596      * </code></pre>
19597      */
19598     getSortState : function(){
19599         return this.sortInfo;
19600     },
19601
19602     // private
19603     applySort : function(){
19604         if(this.sortInfo && !this.remoteSort){
19605             var s = this.sortInfo, f = s.field;
19606             var st = this.fields.get(f).sortType;
19607             var fn = function(r1, r2){
19608                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19609                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19610             };
19611             this.data.sort(s.direction, fn);
19612             if(this.snapshot && this.snapshot != this.data){
19613                 this.snapshot.sort(s.direction, fn);
19614             }
19615         }
19616     },
19617
19618     /**
19619      * Sets the default sort column and order to be used by the next load operation.
19620      * @param {String} fieldName The name of the field to sort by.
19621      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19622      */
19623     setDefaultSort : function(field, dir){
19624         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19625     },
19626
19627     /**
19628      * Sort the Records.
19629      * If remote sorting is used, the sort is performed on the server, and the cache is
19630      * reloaded. If local sorting is used, the cache is sorted internally.
19631      * @param {String} fieldName The name of the field to sort by.
19632      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19633      */
19634     sort : function(fieldName, dir){
19635         var f = this.fields.get(fieldName);
19636         if(!dir){
19637             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19638             
19639             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19640                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19641             }else{
19642                 dir = f.sortDir;
19643             }
19644         }
19645         this.sortToggle[f.name] = dir;
19646         this.sortInfo = {field: f.name, direction: dir};
19647         if(!this.remoteSort){
19648             this.applySort();
19649             this.fireEvent("datachanged", this);
19650         }else{
19651             this.load(this.lastOptions);
19652         }
19653     },
19654
19655     /**
19656      * Calls the specified function for each of the Records in the cache.
19657      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19658      * Returning <em>false</em> aborts and exits the iteration.
19659      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19660      */
19661     each : function(fn, scope){
19662         this.data.each(fn, scope);
19663     },
19664
19665     /**
19666      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19667      * (e.g., during paging).
19668      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19669      */
19670     getModifiedRecords : function(){
19671         return this.modified;
19672     },
19673
19674     // private
19675     createFilterFn : function(property, value, anyMatch){
19676         if(!value.exec){ // not a regex
19677             value = String(value);
19678             if(value.length == 0){
19679                 return false;
19680             }
19681             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19682         }
19683         return function(r){
19684             return value.test(r.data[property]);
19685         };
19686     },
19687
19688     /**
19689      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19690      * @param {String} property A field on your records
19691      * @param {Number} start The record index to start at (defaults to 0)
19692      * @param {Number} end The last record index to include (defaults to length - 1)
19693      * @return {Number} The sum
19694      */
19695     sum : function(property, start, end){
19696         var rs = this.data.items, v = 0;
19697         start = start || 0;
19698         end = (end || end === 0) ? end : rs.length-1;
19699
19700         for(var i = start; i <= end; i++){
19701             v += (rs[i].data[property] || 0);
19702         }
19703         return v;
19704     },
19705
19706     /**
19707      * Filter the records by a specified property.
19708      * @param {String} field A field on your records
19709      * @param {String/RegExp} value Either a string that the field
19710      * should start with or a RegExp to test against the field
19711      * @param {Boolean} anyMatch True to match any part not just the beginning
19712      */
19713     filter : function(property, value, anyMatch){
19714         var fn = this.createFilterFn(property, value, anyMatch);
19715         return fn ? this.filterBy(fn) : this.clearFilter();
19716     },
19717
19718     /**
19719      * Filter by a function. The specified function will be called with each
19720      * record in this data source. If the function returns true the record is included,
19721      * otherwise it is filtered.
19722      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19723      * @param {Object} scope (optional) The scope of the function (defaults to this)
19724      */
19725     filterBy : function(fn, scope){
19726         this.snapshot = this.snapshot || this.data;
19727         this.data = this.queryBy(fn, scope||this);
19728         this.fireEvent("datachanged", this);
19729     },
19730
19731     /**
19732      * Query the records by a specified property.
19733      * @param {String} field A field on your records
19734      * @param {String/RegExp} value Either a string that the field
19735      * should start with or a RegExp to test against the field
19736      * @param {Boolean} anyMatch True to match any part not just the beginning
19737      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19738      */
19739     query : function(property, value, anyMatch){
19740         var fn = this.createFilterFn(property, value, anyMatch);
19741         return fn ? this.queryBy(fn) : this.data.clone();
19742     },
19743
19744     /**
19745      * Query by a function. The specified function will be called with each
19746      * record in this data source. If the function returns true the record is included
19747      * in the results.
19748      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19749      * @param {Object} scope (optional) The scope of the function (defaults to this)
19750       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19751      **/
19752     queryBy : function(fn, scope){
19753         var data = this.snapshot || this.data;
19754         return data.filterBy(fn, scope||this);
19755     },
19756
19757     /**
19758      * Collects unique values for a particular dataIndex from this store.
19759      * @param {String} dataIndex The property to collect
19760      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19761      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19762      * @return {Array} An array of the unique values
19763      **/
19764     collect : function(dataIndex, allowNull, bypassFilter){
19765         var d = (bypassFilter === true && this.snapshot) ?
19766                 this.snapshot.items : this.data.items;
19767         var v, sv, r = [], l = {};
19768         for(var i = 0, len = d.length; i < len; i++){
19769             v = d[i].data[dataIndex];
19770             sv = String(v);
19771             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19772                 l[sv] = true;
19773                 r[r.length] = v;
19774             }
19775         }
19776         return r;
19777     },
19778
19779     /**
19780      * Revert to a view of the Record cache with no filtering applied.
19781      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19782      */
19783     clearFilter : function(suppressEvent){
19784         if(this.snapshot && this.snapshot != this.data){
19785             this.data = this.snapshot;
19786             delete this.snapshot;
19787             if(suppressEvent !== true){
19788                 this.fireEvent("datachanged", this);
19789             }
19790         }
19791     },
19792
19793     // private
19794     afterEdit : function(record){
19795         if(this.modified.indexOf(record) == -1){
19796             this.modified.push(record);
19797         }
19798         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19799     },
19800     
19801     // private
19802     afterReject : function(record){
19803         this.modified.remove(record);
19804         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19805     },
19806
19807     // private
19808     afterCommit : function(record){
19809         this.modified.remove(record);
19810         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19811     },
19812
19813     /**
19814      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19815      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19816      */
19817     commitChanges : function(){
19818         var m = this.modified.slice(0);
19819         this.modified = [];
19820         for(var i = 0, len = m.length; i < len; i++){
19821             m[i].commit();
19822         }
19823     },
19824
19825     /**
19826      * Cancel outstanding changes on all changed records.
19827      */
19828     rejectChanges : function(){
19829         var m = this.modified.slice(0);
19830         this.modified = [];
19831         for(var i = 0, len = m.length; i < len; i++){
19832             m[i].reject();
19833         }
19834     },
19835
19836     onMetaChange : function(meta, rtype, o){
19837         this.recordType = rtype;
19838         this.fields = rtype.prototype.fields;
19839         delete this.snapshot;
19840         this.sortInfo = meta.sortInfo || this.sortInfo;
19841         this.modified = [];
19842         this.fireEvent('metachange', this, this.reader.meta);
19843     }
19844 });/*
19845  * Based on:
19846  * Ext JS Library 1.1.1
19847  * Copyright(c) 2006-2007, Ext JS, LLC.
19848  *
19849  * Originally Released Under LGPL - original licence link has changed is not relivant.
19850  *
19851  * Fork - LGPL
19852  * <script type="text/javascript">
19853  */
19854
19855 /**
19856  * @class Roo.data.SimpleStore
19857  * @extends Roo.data.Store
19858  * Small helper class to make creating Stores from Array data easier.
19859  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19860  * @cfg {Array} fields An array of field definition objects, or field name strings.
19861  * @cfg {Array} data The multi-dimensional array of data
19862  * @constructor
19863  * @param {Object} config
19864  */
19865 Roo.data.SimpleStore = function(config){
19866     Roo.data.SimpleStore.superclass.constructor.call(this, {
19867         isLocal : true,
19868         reader: new Roo.data.ArrayReader({
19869                 id: config.id
19870             },
19871             Roo.data.Record.create(config.fields)
19872         ),
19873         proxy : new Roo.data.MemoryProxy(config.data)
19874     });
19875     this.load();
19876 };
19877 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19878  * Based on:
19879  * Ext JS Library 1.1.1
19880  * Copyright(c) 2006-2007, Ext JS, LLC.
19881  *
19882  * Originally Released Under LGPL - original licence link has changed is not relivant.
19883  *
19884  * Fork - LGPL
19885  * <script type="text/javascript">
19886  */
19887
19888 /**
19889 /**
19890  * @extends Roo.data.Store
19891  * @class Roo.data.JsonStore
19892  * Small helper class to make creating Stores for JSON data easier. <br/>
19893 <pre><code>
19894 var store = new Roo.data.JsonStore({
19895     url: 'get-images.php',
19896     root: 'images',
19897     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19898 });
19899 </code></pre>
19900  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19901  * JsonReader and HttpProxy (unless inline data is provided).</b>
19902  * @cfg {Array} fields An array of field definition objects, or field name strings.
19903  * @constructor
19904  * @param {Object} config
19905  */
19906 Roo.data.JsonStore = function(c){
19907     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19908         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19909         reader: new Roo.data.JsonReader(c, c.fields)
19910     }));
19911 };
19912 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19913  * Based on:
19914  * Ext JS Library 1.1.1
19915  * Copyright(c) 2006-2007, Ext JS, LLC.
19916  *
19917  * Originally Released Under LGPL - original licence link has changed is not relivant.
19918  *
19919  * Fork - LGPL
19920  * <script type="text/javascript">
19921  */
19922
19923  
19924 Roo.data.Field = function(config){
19925     if(typeof config == "string"){
19926         config = {name: config};
19927     }
19928     Roo.apply(this, config);
19929     
19930     if(!this.type){
19931         this.type = "auto";
19932     }
19933     
19934     var st = Roo.data.SortTypes;
19935     // named sortTypes are supported, here we look them up
19936     if(typeof this.sortType == "string"){
19937         this.sortType = st[this.sortType];
19938     }
19939     
19940     // set default sortType for strings and dates
19941     if(!this.sortType){
19942         switch(this.type){
19943             case "string":
19944                 this.sortType = st.asUCString;
19945                 break;
19946             case "date":
19947                 this.sortType = st.asDate;
19948                 break;
19949             default:
19950                 this.sortType = st.none;
19951         }
19952     }
19953
19954     // define once
19955     var stripRe = /[\$,%]/g;
19956
19957     // prebuilt conversion function for this field, instead of
19958     // switching every time we're reading a value
19959     if(!this.convert){
19960         var cv, dateFormat = this.dateFormat;
19961         switch(this.type){
19962             case "":
19963             case "auto":
19964             case undefined:
19965                 cv = function(v){ return v; };
19966                 break;
19967             case "string":
19968                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19969                 break;
19970             case "int":
19971                 cv = function(v){
19972                     return v !== undefined && v !== null && v !== '' ?
19973                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19974                     };
19975                 break;
19976             case "float":
19977                 cv = function(v){
19978                     return v !== undefined && v !== null && v !== '' ?
19979                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19980                     };
19981                 break;
19982             case "bool":
19983             case "boolean":
19984                 cv = function(v){ return v === true || v === "true" || v == 1; };
19985                 break;
19986             case "date":
19987                 cv = function(v){
19988                     if(!v){
19989                         return '';
19990                     }
19991                     if(v instanceof Date){
19992                         return v;
19993                     }
19994                     if(dateFormat){
19995                         if(dateFormat == "timestamp"){
19996                             return new Date(v*1000);
19997                         }
19998                         return Date.parseDate(v, dateFormat);
19999                     }
20000                     var parsed = Date.parse(v);
20001                     return parsed ? new Date(parsed) : null;
20002                 };
20003              break;
20004             
20005         }
20006         this.convert = cv;
20007     }
20008 };
20009
20010 Roo.data.Field.prototype = {
20011     dateFormat: null,
20012     defaultValue: "",
20013     mapping: null,
20014     sortType : null,
20015     sortDir : "ASC"
20016 };/*
20017  * Based on:
20018  * Ext JS Library 1.1.1
20019  * Copyright(c) 2006-2007, Ext JS, LLC.
20020  *
20021  * Originally Released Under LGPL - original licence link has changed is not relivant.
20022  *
20023  * Fork - LGPL
20024  * <script type="text/javascript">
20025  */
20026  
20027 // Base class for reading structured data from a data source.  This class is intended to be
20028 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20029
20030 /**
20031  * @class Roo.data.DataReader
20032  * Base class for reading structured data from a data source.  This class is intended to be
20033  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20034  */
20035
20036 Roo.data.DataReader = function(meta, recordType){
20037     
20038     this.meta = meta;
20039     
20040     this.recordType = recordType instanceof Array ? 
20041         Roo.data.Record.create(recordType) : recordType;
20042 };
20043
20044 Roo.data.DataReader.prototype = {
20045      /**
20046      * Create an empty record
20047      * @param {Object} data (optional) - overlay some values
20048      * @return {Roo.data.Record} record created.
20049      */
20050     newRow :  function(d) {
20051         var da =  {};
20052         this.recordType.prototype.fields.each(function(c) {
20053             switch( c.type) {
20054                 case 'int' : da[c.name] = 0; break;
20055                 case 'date' : da[c.name] = new Date(); break;
20056                 case 'float' : da[c.name] = 0.0; break;
20057                 case 'boolean' : da[c.name] = false; break;
20058                 default : da[c.name] = ""; break;
20059             }
20060             
20061         });
20062         return new this.recordType(Roo.apply(da, d));
20063     }
20064     
20065 };/*
20066  * Based on:
20067  * Ext JS Library 1.1.1
20068  * Copyright(c) 2006-2007, Ext JS, LLC.
20069  *
20070  * Originally Released Under LGPL - original licence link has changed is not relivant.
20071  *
20072  * Fork - LGPL
20073  * <script type="text/javascript">
20074  */
20075
20076 /**
20077  * @class Roo.data.DataProxy
20078  * @extends Roo.data.Observable
20079  * This class is an abstract base class for implementations which provide retrieval of
20080  * unformatted data objects.<br>
20081  * <p>
20082  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20083  * (of the appropriate type which knows how to parse the data object) to provide a block of
20084  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20085  * <p>
20086  * Custom implementations must implement the load method as described in
20087  * {@link Roo.data.HttpProxy#load}.
20088  */
20089 Roo.data.DataProxy = function(){
20090     this.addEvents({
20091         /**
20092          * @event beforeload
20093          * Fires before a network request is made to retrieve a data object.
20094          * @param {Object} This DataProxy object.
20095          * @param {Object} params The params parameter to the load function.
20096          */
20097         beforeload : true,
20098         /**
20099          * @event load
20100          * Fires before the load method's callback is called.
20101          * @param {Object} This DataProxy object.
20102          * @param {Object} o The data object.
20103          * @param {Object} arg The callback argument object passed to the load function.
20104          */
20105         load : true,
20106         /**
20107          * @event loadexception
20108          * Fires if an Exception occurs during data retrieval.
20109          * @param {Object} This DataProxy object.
20110          * @param {Object} o The data object.
20111          * @param {Object} arg The callback argument object passed to the load function.
20112          * @param {Object} e The Exception.
20113          */
20114         loadexception : true
20115     });
20116     Roo.data.DataProxy.superclass.constructor.call(this);
20117 };
20118
20119 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20120
20121     /**
20122      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20123      */
20124 /*
20125  * Based on:
20126  * Ext JS Library 1.1.1
20127  * Copyright(c) 2006-2007, Ext JS, LLC.
20128  *
20129  * Originally Released Under LGPL - original licence link has changed is not relivant.
20130  *
20131  * Fork - LGPL
20132  * <script type="text/javascript">
20133  */
20134 /**
20135  * @class Roo.data.MemoryProxy
20136  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20137  * to the Reader when its load method is called.
20138  * @constructor
20139  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20140  */
20141 Roo.data.MemoryProxy = function(data){
20142     if (data.data) {
20143         data = data.data;
20144     }
20145     Roo.data.MemoryProxy.superclass.constructor.call(this);
20146     this.data = data;
20147 };
20148
20149 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20150     /**
20151      * Load data from the requested source (in this case an in-memory
20152      * data object passed to the constructor), read the data object into
20153      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20154      * process that block using the passed callback.
20155      * @param {Object} params This parameter is not used by the MemoryProxy class.
20156      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20157      * object into a block of Roo.data.Records.
20158      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20159      * The function must be passed <ul>
20160      * <li>The Record block object</li>
20161      * <li>The "arg" argument from the load function</li>
20162      * <li>A boolean success indicator</li>
20163      * </ul>
20164      * @param {Object} scope The scope in which to call the callback
20165      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20166      */
20167     load : function(params, reader, callback, scope, arg){
20168         params = params || {};
20169         var result;
20170         try {
20171             result = reader.readRecords(this.data);
20172         }catch(e){
20173             this.fireEvent("loadexception", this, arg, null, e);
20174             callback.call(scope, null, arg, false);
20175             return;
20176         }
20177         callback.call(scope, result, arg, true);
20178     },
20179     
20180     // private
20181     update : function(params, records){
20182         
20183     }
20184 });/*
20185  * Based on:
20186  * Ext JS Library 1.1.1
20187  * Copyright(c) 2006-2007, Ext JS, LLC.
20188  *
20189  * Originally Released Under LGPL - original licence link has changed is not relivant.
20190  *
20191  * Fork - LGPL
20192  * <script type="text/javascript">
20193  */
20194 /**
20195  * @class Roo.data.HttpProxy
20196  * @extends Roo.data.DataProxy
20197  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20198  * configured to reference a certain URL.<br><br>
20199  * <p>
20200  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20201  * from which the running page was served.<br><br>
20202  * <p>
20203  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20204  * <p>
20205  * Be aware that to enable the browser to parse an XML document, the server must set
20206  * the Content-Type header in the HTTP response to "text/xml".
20207  * @constructor
20208  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20209  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20210  * will be used to make the request.
20211  */
20212 Roo.data.HttpProxy = function(conn){
20213     Roo.data.HttpProxy.superclass.constructor.call(this);
20214     // is conn a conn config or a real conn?
20215     this.conn = conn;
20216     this.useAjax = !conn || !conn.events;
20217   
20218 };
20219
20220 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20221     // thse are take from connection...
20222     
20223     /**
20224      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20225      */
20226     /**
20227      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20228      * extra parameters to each request made by this object. (defaults to undefined)
20229      */
20230     /**
20231      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20232      *  to each request made by this object. (defaults to undefined)
20233      */
20234     /**
20235      * @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)
20236      */
20237     /**
20238      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20239      */
20240      /**
20241      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20242      * @type Boolean
20243      */
20244   
20245
20246     /**
20247      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20248      * @type Boolean
20249      */
20250     /**
20251      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20252      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20253      * a finer-grained basis than the DataProxy events.
20254      */
20255     getConnection : function(){
20256         return this.useAjax ? Roo.Ajax : this.conn;
20257     },
20258
20259     /**
20260      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20261      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20262      * process that block using the passed callback.
20263      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20264      * for the request to the remote server.
20265      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20266      * object into a block of Roo.data.Records.
20267      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20268      * The function must be passed <ul>
20269      * <li>The Record block object</li>
20270      * <li>The "arg" argument from the load function</li>
20271      * <li>A boolean success indicator</li>
20272      * </ul>
20273      * @param {Object} scope The scope in which to call the callback
20274      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20275      */
20276     load : function(params, reader, callback, scope, arg){
20277         if(this.fireEvent("beforeload", this, params) !== false){
20278             var  o = {
20279                 params : params || {},
20280                 request: {
20281                     callback : callback,
20282                     scope : scope,
20283                     arg : arg
20284                 },
20285                 reader: reader,
20286                 callback : this.loadResponse,
20287                 scope: this
20288             };
20289             if(this.useAjax){
20290                 Roo.applyIf(o, this.conn);
20291                 if(this.activeRequest){
20292                     Roo.Ajax.abort(this.activeRequest);
20293                 }
20294                 this.activeRequest = Roo.Ajax.request(o);
20295             }else{
20296                 this.conn.request(o);
20297             }
20298         }else{
20299             callback.call(scope||this, null, arg, false);
20300         }
20301     },
20302
20303     // private
20304     loadResponse : function(o, success, response){
20305         delete this.activeRequest;
20306         if(!success){
20307             this.fireEvent("loadexception", this, o, response);
20308             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20309             return;
20310         }
20311         var result;
20312         try {
20313             result = o.reader.read(response);
20314         }catch(e){
20315             this.fireEvent("loadexception", this, o, response, e);
20316             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20317             return;
20318         }
20319         
20320         this.fireEvent("load", this, o, o.request.arg);
20321         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20322     },
20323
20324     // private
20325     update : function(dataSet){
20326
20327     },
20328
20329     // private
20330     updateResponse : function(dataSet){
20331
20332     }
20333 });/*
20334  * Based on:
20335  * Ext JS Library 1.1.1
20336  * Copyright(c) 2006-2007, Ext JS, LLC.
20337  *
20338  * Originally Released Under LGPL - original licence link has changed is not relivant.
20339  *
20340  * Fork - LGPL
20341  * <script type="text/javascript">
20342  */
20343
20344 /**
20345  * @class Roo.data.ScriptTagProxy
20346  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20347  * other than the originating domain of the running page.<br><br>
20348  * <p>
20349  * <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
20350  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20351  * <p>
20352  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20353  * source code that is used as the source inside a &lt;script> tag.<br><br>
20354  * <p>
20355  * In order for the browser to process the returned data, the server must wrap the data object
20356  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20357  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20358  * depending on whether the callback name was passed:
20359  * <p>
20360  * <pre><code>
20361 boolean scriptTag = false;
20362 String cb = request.getParameter("callback");
20363 if (cb != null) {
20364     scriptTag = true;
20365     response.setContentType("text/javascript");
20366 } else {
20367     response.setContentType("application/x-json");
20368 }
20369 Writer out = response.getWriter();
20370 if (scriptTag) {
20371     out.write(cb + "(");
20372 }
20373 out.print(dataBlock.toJsonString());
20374 if (scriptTag) {
20375     out.write(");");
20376 }
20377 </pre></code>
20378  *
20379  * @constructor
20380  * @param {Object} config A configuration object.
20381  */
20382 Roo.data.ScriptTagProxy = function(config){
20383     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20384     Roo.apply(this, config);
20385     this.head = document.getElementsByTagName("head")[0];
20386 };
20387
20388 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20389
20390 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20391     /**
20392      * @cfg {String} url The URL from which to request the data object.
20393      */
20394     /**
20395      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20396      */
20397     timeout : 30000,
20398     /**
20399      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20400      * the server the name of the callback function set up by the load call to process the returned data object.
20401      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20402      * javascript output which calls this named function passing the data object as its only parameter.
20403      */
20404     callbackParam : "callback",
20405     /**
20406      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20407      * name to the request.
20408      */
20409     nocache : true,
20410
20411     /**
20412      * Load data from the configured URL, read the data object into
20413      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20414      * process that block using the passed callback.
20415      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20416      * for the request to the remote server.
20417      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20418      * object into a block of Roo.data.Records.
20419      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20420      * The function must be passed <ul>
20421      * <li>The Record block object</li>
20422      * <li>The "arg" argument from the load function</li>
20423      * <li>A boolean success indicator</li>
20424      * </ul>
20425      * @param {Object} scope The scope in which to call the callback
20426      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20427      */
20428     load : function(params, reader, callback, scope, arg){
20429         if(this.fireEvent("beforeload", this, params) !== false){
20430
20431             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20432
20433             var url = this.url;
20434             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20435             if(this.nocache){
20436                 url += "&_dc=" + (new Date().getTime());
20437             }
20438             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20439             var trans = {
20440                 id : transId,
20441                 cb : "stcCallback"+transId,
20442                 scriptId : "stcScript"+transId,
20443                 params : params,
20444                 arg : arg,
20445                 url : url,
20446                 callback : callback,
20447                 scope : scope,
20448                 reader : reader
20449             };
20450             var conn = this;
20451
20452             window[trans.cb] = function(o){
20453                 conn.handleResponse(o, trans);
20454             };
20455
20456             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20457
20458             if(this.autoAbort !== false){
20459                 this.abort();
20460             }
20461
20462             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20463
20464             var script = document.createElement("script");
20465             script.setAttribute("src", url);
20466             script.setAttribute("type", "text/javascript");
20467             script.setAttribute("id", trans.scriptId);
20468             this.head.appendChild(script);
20469
20470             this.trans = trans;
20471         }else{
20472             callback.call(scope||this, null, arg, false);
20473         }
20474     },
20475
20476     // private
20477     isLoading : function(){
20478         return this.trans ? true : false;
20479     },
20480
20481     /**
20482      * Abort the current server request.
20483      */
20484     abort : function(){
20485         if(this.isLoading()){
20486             this.destroyTrans(this.trans);
20487         }
20488     },
20489
20490     // private
20491     destroyTrans : function(trans, isLoaded){
20492         this.head.removeChild(document.getElementById(trans.scriptId));
20493         clearTimeout(trans.timeoutId);
20494         if(isLoaded){
20495             window[trans.cb] = undefined;
20496             try{
20497                 delete window[trans.cb];
20498             }catch(e){}
20499         }else{
20500             // if hasn't been loaded, wait for load to remove it to prevent script error
20501             window[trans.cb] = function(){
20502                 window[trans.cb] = undefined;
20503                 try{
20504                     delete window[trans.cb];
20505                 }catch(e){}
20506             };
20507         }
20508     },
20509
20510     // private
20511     handleResponse : function(o, trans){
20512         this.trans = false;
20513         this.destroyTrans(trans, true);
20514         var result;
20515         try {
20516             result = trans.reader.readRecords(o);
20517         }catch(e){
20518             this.fireEvent("loadexception", this, o, trans.arg, e);
20519             trans.callback.call(trans.scope||window, null, trans.arg, false);
20520             return;
20521         }
20522         this.fireEvent("load", this, o, trans.arg);
20523         trans.callback.call(trans.scope||window, result, trans.arg, true);
20524     },
20525
20526     // private
20527     handleFailure : function(trans){
20528         this.trans = false;
20529         this.destroyTrans(trans, false);
20530         this.fireEvent("loadexception", this, null, trans.arg);
20531         trans.callback.call(trans.scope||window, null, trans.arg, false);
20532     }
20533 });/*
20534  * Based on:
20535  * Ext JS Library 1.1.1
20536  * Copyright(c) 2006-2007, Ext JS, LLC.
20537  *
20538  * Originally Released Under LGPL - original licence link has changed is not relivant.
20539  *
20540  * Fork - LGPL
20541  * <script type="text/javascript">
20542  */
20543
20544 /**
20545  * @class Roo.data.JsonReader
20546  * @extends Roo.data.DataReader
20547  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20548  * based on mappings in a provided Roo.data.Record constructor.
20549  * 
20550  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20551  * in the reply previously. 
20552  * 
20553  * <p>
20554  * Example code:
20555  * <pre><code>
20556 var RecordDef = Roo.data.Record.create([
20557     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20558     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20559 ]);
20560 var myReader = new Roo.data.JsonReader({
20561     totalProperty: "results",    // The property which contains the total dataset size (optional)
20562     root: "rows",                // The property which contains an Array of row objects
20563     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20564 }, RecordDef);
20565 </code></pre>
20566  * <p>
20567  * This would consume a JSON file like this:
20568  * <pre><code>
20569 { 'results': 2, 'rows': [
20570     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20571     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20572 }
20573 </code></pre>
20574  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20575  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20576  * paged from the remote server.
20577  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20578  * @cfg {String} root name of the property which contains the Array of row objects.
20579  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20580  * @constructor
20581  * Create a new JsonReader
20582  * @param {Object} meta Metadata configuration options
20583  * @param {Object} recordType Either an Array of field definition objects,
20584  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20585  */
20586 Roo.data.JsonReader = function(meta, recordType){
20587     
20588     meta = meta || {};
20589     // set some defaults:
20590     Roo.applyIf(meta, {
20591         totalProperty: 'total',
20592         successProperty : 'success',
20593         root : 'data',
20594         id : 'id'
20595     });
20596     
20597     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20598 };
20599 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20600     
20601     /**
20602      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20603      * Used by Store query builder to append _requestMeta to params.
20604      * 
20605      */
20606     metaFromRemote : false,
20607     /**
20608      * This method is only used by a DataProxy which has retrieved data from a remote server.
20609      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20610      * @return {Object} data A data block which is used by an Roo.data.Store object as
20611      * a cache of Roo.data.Records.
20612      */
20613     read : function(response){
20614         var json = response.responseText;
20615        
20616         var o = /* eval:var:o */ eval("("+json+")");
20617         if(!o) {
20618             throw {message: "JsonReader.read: Json object not found"};
20619         }
20620         
20621         if(o.metaData){
20622             
20623             delete this.ef;
20624             this.metaFromRemote = true;
20625             this.meta = o.metaData;
20626             this.recordType = Roo.data.Record.create(o.metaData.fields);
20627             this.onMetaChange(this.meta, this.recordType, o);
20628         }
20629         return this.readRecords(o);
20630     },
20631
20632     // private function a store will implement
20633     onMetaChange : function(meta, recordType, o){
20634
20635     },
20636
20637     /**
20638          * @ignore
20639          */
20640     simpleAccess: function(obj, subsc) {
20641         return obj[subsc];
20642     },
20643
20644         /**
20645          * @ignore
20646          */
20647     getJsonAccessor: function(){
20648         var re = /[\[\.]/;
20649         return function(expr) {
20650             try {
20651                 return(re.test(expr))
20652                     ? new Function("obj", "return obj." + expr)
20653                     : function(obj){
20654                         return obj[expr];
20655                     };
20656             } catch(e){}
20657             return Roo.emptyFn;
20658         };
20659     }(),
20660
20661     /**
20662      * Create a data block containing Roo.data.Records from an XML document.
20663      * @param {Object} o An object which contains an Array of row objects in the property specified
20664      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20665      * which contains the total size of the dataset.
20666      * @return {Object} data A data block which is used by an Roo.data.Store object as
20667      * a cache of Roo.data.Records.
20668      */
20669     readRecords : function(o){
20670         /**
20671          * After any data loads, the raw JSON data is available for further custom processing.
20672          * @type Object
20673          */
20674         this.jsonData = o;
20675         var s = this.meta, Record = this.recordType,
20676             f = Record.prototype.fields, fi = f.items, fl = f.length;
20677
20678 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20679         if (!this.ef) {
20680             if(s.totalProperty) {
20681                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20682                 }
20683                 if(s.successProperty) {
20684                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20685                 }
20686                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20687                 if (s.id) {
20688                         var g = this.getJsonAccessor(s.id);
20689                         this.getId = function(rec) {
20690                                 var r = g(rec);
20691                                 return (r === undefined || r === "") ? null : r;
20692                         };
20693                 } else {
20694                         this.getId = function(){return null;};
20695                 }
20696             this.ef = [];
20697             for(var jj = 0; jj < fl; jj++){
20698                 f = fi[jj];
20699                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20700                 this.ef[jj] = this.getJsonAccessor(map);
20701             }
20702         }
20703
20704         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20705         if(s.totalProperty){
20706             var vt = parseInt(this.getTotal(o), 10);
20707             if(!isNaN(vt)){
20708                 totalRecords = vt;
20709             }
20710         }
20711         if(s.successProperty){
20712             var vs = this.getSuccess(o);
20713             if(vs === false || vs === 'false'){
20714                 success = false;
20715             }
20716         }
20717         var records = [];
20718             for(var i = 0; i < c; i++){
20719                     var n = root[i];
20720                 var values = {};
20721                 var id = this.getId(n);
20722                 for(var j = 0; j < fl; j++){
20723                     f = fi[j];
20724                 var v = this.ef[j](n);
20725                 if (!f.convert) {
20726                     Roo.log('missing convert for ' + f.name);
20727                     Roo.log(f);
20728                     continue;
20729                 }
20730                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20731                 }
20732                 var record = new Record(values, id);
20733                 record.json = n;
20734                 records[i] = record;
20735             }
20736             return {
20737                 success : success,
20738                 records : records,
20739                 totalRecords : totalRecords
20740             };
20741     }
20742 });/*
20743  * Based on:
20744  * Ext JS Library 1.1.1
20745  * Copyright(c) 2006-2007, Ext JS, LLC.
20746  *
20747  * Originally Released Under LGPL - original licence link has changed is not relivant.
20748  *
20749  * Fork - LGPL
20750  * <script type="text/javascript">
20751  */
20752
20753 /**
20754  * @class Roo.data.XmlReader
20755  * @extends Roo.data.DataReader
20756  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20757  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20758  * <p>
20759  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20760  * header in the HTTP response must be set to "text/xml".</em>
20761  * <p>
20762  * Example code:
20763  * <pre><code>
20764 var RecordDef = Roo.data.Record.create([
20765    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20766    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20767 ]);
20768 var myReader = new Roo.data.XmlReader({
20769    totalRecords: "results", // The element which contains the total dataset size (optional)
20770    record: "row",           // The repeated element which contains row information
20771    id: "id"                 // The element within the row that provides an ID for the record (optional)
20772 }, RecordDef);
20773 </code></pre>
20774  * <p>
20775  * This would consume an XML file like this:
20776  * <pre><code>
20777 &lt;?xml?>
20778 &lt;dataset>
20779  &lt;results>2&lt;/results>
20780  &lt;row>
20781    &lt;id>1&lt;/id>
20782    &lt;name>Bill&lt;/name>
20783    &lt;occupation>Gardener&lt;/occupation>
20784  &lt;/row>
20785  &lt;row>
20786    &lt;id>2&lt;/id>
20787    &lt;name>Ben&lt;/name>
20788    &lt;occupation>Horticulturalist&lt;/occupation>
20789  &lt;/row>
20790 &lt;/dataset>
20791 </code></pre>
20792  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20793  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20794  * paged from the remote server.
20795  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20796  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20797  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20798  * a record identifier value.
20799  * @constructor
20800  * Create a new XmlReader
20801  * @param {Object} meta Metadata configuration options
20802  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20803  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20804  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20805  */
20806 Roo.data.XmlReader = function(meta, recordType){
20807     meta = meta || {};
20808     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20809 };
20810 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20811     /**
20812      * This method is only used by a DataProxy which has retrieved data from a remote server.
20813          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20814          * to contain a method called 'responseXML' that returns an XML document object.
20815      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20816      * a cache of Roo.data.Records.
20817      */
20818     read : function(response){
20819         var doc = response.responseXML;
20820         if(!doc) {
20821             throw {message: "XmlReader.read: XML Document not available"};
20822         }
20823         return this.readRecords(doc);
20824     },
20825
20826     /**
20827      * Create a data block containing Roo.data.Records from an XML document.
20828          * @param {Object} doc A parsed XML document.
20829      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20830      * a cache of Roo.data.Records.
20831      */
20832     readRecords : function(doc){
20833         /**
20834          * After any data loads/reads, the raw XML Document is available for further custom processing.
20835          * @type XMLDocument
20836          */
20837         this.xmlData = doc;
20838         var root = doc.documentElement || doc;
20839         var q = Roo.DomQuery;
20840         var recordType = this.recordType, fields = recordType.prototype.fields;
20841         var sid = this.meta.id;
20842         var totalRecords = 0, success = true;
20843         if(this.meta.totalRecords){
20844             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20845         }
20846         
20847         if(this.meta.success){
20848             var sv = q.selectValue(this.meta.success, root, true);
20849             success = sv !== false && sv !== 'false';
20850         }
20851         var records = [];
20852         var ns = q.select(this.meta.record, root);
20853         for(var i = 0, len = ns.length; i < len; i++) {
20854                 var n = ns[i];
20855                 var values = {};
20856                 var id = sid ? q.selectValue(sid, n) : undefined;
20857                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20858                     var f = fields.items[j];
20859                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20860                     v = f.convert(v);
20861                     values[f.name] = v;
20862                 }
20863                 var record = new recordType(values, id);
20864                 record.node = n;
20865                 records[records.length] = record;
20866             }
20867
20868             return {
20869                 success : success,
20870                 records : records,
20871                 totalRecords : totalRecords || records.length
20872             };
20873     }
20874 });/*
20875  * Based on:
20876  * Ext JS Library 1.1.1
20877  * Copyright(c) 2006-2007, Ext JS, LLC.
20878  *
20879  * Originally Released Under LGPL - original licence link has changed is not relivant.
20880  *
20881  * Fork - LGPL
20882  * <script type="text/javascript">
20883  */
20884
20885 /**
20886  * @class Roo.data.ArrayReader
20887  * @extends Roo.data.DataReader
20888  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20889  * Each element of that Array represents a row of data fields. The
20890  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20891  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20892  * <p>
20893  * Example code:.
20894  * <pre><code>
20895 var RecordDef = Roo.data.Record.create([
20896     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20897     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20898 ]);
20899 var myReader = new Roo.data.ArrayReader({
20900     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20901 }, RecordDef);
20902 </code></pre>
20903  * <p>
20904  * This would consume an Array like this:
20905  * <pre><code>
20906 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20907   </code></pre>
20908  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20909  * @constructor
20910  * Create a new JsonReader
20911  * @param {Object} meta Metadata configuration options.
20912  * @param {Object} recordType Either an Array of field definition objects
20913  * as specified to {@link Roo.data.Record#create},
20914  * or an {@link Roo.data.Record} object
20915  * created using {@link Roo.data.Record#create}.
20916  */
20917 Roo.data.ArrayReader = function(meta, recordType){
20918     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20919 };
20920
20921 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20922     /**
20923      * Create a data block containing Roo.data.Records from an XML document.
20924      * @param {Object} o An Array of row objects which represents the dataset.
20925      * @return {Object} data A data block which is used by an Roo.data.Store object as
20926      * a cache of Roo.data.Records.
20927      */
20928     readRecords : function(o){
20929         var sid = this.meta ? this.meta.id : null;
20930         var recordType = this.recordType, fields = recordType.prototype.fields;
20931         var records = [];
20932         var root = o;
20933             for(var i = 0; i < root.length; i++){
20934                     var n = root[i];
20935                 var values = {};
20936                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20937                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20938                 var f = fields.items[j];
20939                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20940                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20941                 v = f.convert(v);
20942                 values[f.name] = v;
20943             }
20944                 var record = new recordType(values, id);
20945                 record.json = n;
20946                 records[records.length] = record;
20947             }
20948             return {
20949                 records : records,
20950                 totalRecords : records.length
20951             };
20952     }
20953 });/*
20954  * Based on:
20955  * Ext JS Library 1.1.1
20956  * Copyright(c) 2006-2007, Ext JS, LLC.
20957  *
20958  * Originally Released Under LGPL - original licence link has changed is not relivant.
20959  *
20960  * Fork - LGPL
20961  * <script type="text/javascript">
20962  */
20963
20964
20965 /**
20966  * @class Roo.data.Tree
20967  * @extends Roo.util.Observable
20968  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20969  * in the tree have most standard DOM functionality.
20970  * @constructor
20971  * @param {Node} root (optional) The root node
20972  */
20973 Roo.data.Tree = function(root){
20974    this.nodeHash = {};
20975    /**
20976     * The root node for this tree
20977     * @type Node
20978     */
20979    this.root = null;
20980    if(root){
20981        this.setRootNode(root);
20982    }
20983    this.addEvents({
20984        /**
20985         * @event append
20986         * Fires when a new child node is appended to a node in this tree.
20987         * @param {Tree} tree The owner tree
20988         * @param {Node} parent The parent node
20989         * @param {Node} node The newly appended node
20990         * @param {Number} index The index of the newly appended node
20991         */
20992        "append" : true,
20993        /**
20994         * @event remove
20995         * Fires when a child node is removed from a node in this tree.
20996         * @param {Tree} tree The owner tree
20997         * @param {Node} parent The parent node
20998         * @param {Node} node The child node removed
20999         */
21000        "remove" : true,
21001        /**
21002         * @event move
21003         * Fires when a node is moved to a new location in the tree
21004         * @param {Tree} tree The owner tree
21005         * @param {Node} node The node moved
21006         * @param {Node} oldParent The old parent of this node
21007         * @param {Node} newParent The new parent of this node
21008         * @param {Number} index The index it was moved to
21009         */
21010        "move" : true,
21011        /**
21012         * @event insert
21013         * Fires when a new child node is inserted in a node in this tree.
21014         * @param {Tree} tree The owner tree
21015         * @param {Node} parent The parent node
21016         * @param {Node} node The child node inserted
21017         * @param {Node} refNode The child node the node was inserted before
21018         */
21019        "insert" : true,
21020        /**
21021         * @event beforeappend
21022         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21023         * @param {Tree} tree The owner tree
21024         * @param {Node} parent The parent node
21025         * @param {Node} node The child node to be appended
21026         */
21027        "beforeappend" : true,
21028        /**
21029         * @event beforeremove
21030         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21031         * @param {Tree} tree The owner tree
21032         * @param {Node} parent The parent node
21033         * @param {Node} node The child node to be removed
21034         */
21035        "beforeremove" : true,
21036        /**
21037         * @event beforemove
21038         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21039         * @param {Tree} tree The owner tree
21040         * @param {Node} node The node being moved
21041         * @param {Node} oldParent The parent of the node
21042         * @param {Node} newParent The new parent the node is moving to
21043         * @param {Number} index The index it is being moved to
21044         */
21045        "beforemove" : true,
21046        /**
21047         * @event beforeinsert
21048         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21049         * @param {Tree} tree The owner tree
21050         * @param {Node} parent The parent node
21051         * @param {Node} node The child node to be inserted
21052         * @param {Node} refNode The child node the node is being inserted before
21053         */
21054        "beforeinsert" : true
21055    });
21056
21057     Roo.data.Tree.superclass.constructor.call(this);
21058 };
21059
21060 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21061     pathSeparator: "/",
21062
21063     proxyNodeEvent : function(){
21064         return this.fireEvent.apply(this, arguments);
21065     },
21066
21067     /**
21068      * Returns the root node for this tree.
21069      * @return {Node}
21070      */
21071     getRootNode : function(){
21072         return this.root;
21073     },
21074
21075     /**
21076      * Sets the root node for this tree.
21077      * @param {Node} node
21078      * @return {Node}
21079      */
21080     setRootNode : function(node){
21081         this.root = node;
21082         node.ownerTree = this;
21083         node.isRoot = true;
21084         this.registerNode(node);
21085         return node;
21086     },
21087
21088     /**
21089      * Gets a node in this tree by its id.
21090      * @param {String} id
21091      * @return {Node}
21092      */
21093     getNodeById : function(id){
21094         return this.nodeHash[id];
21095     },
21096
21097     registerNode : function(node){
21098         this.nodeHash[node.id] = node;
21099     },
21100
21101     unregisterNode : function(node){
21102         delete this.nodeHash[node.id];
21103     },
21104
21105     toString : function(){
21106         return "[Tree"+(this.id?" "+this.id:"")+"]";
21107     }
21108 });
21109
21110 /**
21111  * @class Roo.data.Node
21112  * @extends Roo.util.Observable
21113  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21114  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21115  * @constructor
21116  * @param {Object} attributes The attributes/config for the node
21117  */
21118 Roo.data.Node = function(attributes){
21119     /**
21120      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21121      * @type {Object}
21122      */
21123     this.attributes = attributes || {};
21124     this.leaf = this.attributes.leaf;
21125     /**
21126      * The node id. @type String
21127      */
21128     this.id = this.attributes.id;
21129     if(!this.id){
21130         this.id = Roo.id(null, "ynode-");
21131         this.attributes.id = this.id;
21132     }
21133     /**
21134      * All child nodes of this node. @type Array
21135      */
21136     this.childNodes = [];
21137     if(!this.childNodes.indexOf){ // indexOf is a must
21138         this.childNodes.indexOf = function(o){
21139             for(var i = 0, len = this.length; i < len; i++){
21140                 if(this[i] == o) {
21141                     return i;
21142                 }
21143             }
21144             return -1;
21145         };
21146     }
21147     /**
21148      * The parent node for this node. @type Node
21149      */
21150     this.parentNode = null;
21151     /**
21152      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21153      */
21154     this.firstChild = null;
21155     /**
21156      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21157      */
21158     this.lastChild = null;
21159     /**
21160      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21161      */
21162     this.previousSibling = null;
21163     /**
21164      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21165      */
21166     this.nextSibling = null;
21167
21168     this.addEvents({
21169        /**
21170         * @event append
21171         * Fires when a new child node is appended
21172         * @param {Tree} tree The owner tree
21173         * @param {Node} this This node
21174         * @param {Node} node The newly appended node
21175         * @param {Number} index The index of the newly appended node
21176         */
21177        "append" : true,
21178        /**
21179         * @event remove
21180         * Fires when a child node is removed
21181         * @param {Tree} tree The owner tree
21182         * @param {Node} this This node
21183         * @param {Node} node The removed node
21184         */
21185        "remove" : true,
21186        /**
21187         * @event move
21188         * Fires when this node is moved to a new location in the tree
21189         * @param {Tree} tree The owner tree
21190         * @param {Node} this This node
21191         * @param {Node} oldParent The old parent of this node
21192         * @param {Node} newParent The new parent of this node
21193         * @param {Number} index The index it was moved to
21194         */
21195        "move" : true,
21196        /**
21197         * @event insert
21198         * Fires when a new child node is inserted.
21199         * @param {Tree} tree The owner tree
21200         * @param {Node} this This node
21201         * @param {Node} node The child node inserted
21202         * @param {Node} refNode The child node the node was inserted before
21203         */
21204        "insert" : true,
21205        /**
21206         * @event beforeappend
21207         * Fires before a new child is appended, return false to cancel the append.
21208         * @param {Tree} tree The owner tree
21209         * @param {Node} this This node
21210         * @param {Node} node The child node to be appended
21211         */
21212        "beforeappend" : true,
21213        /**
21214         * @event beforeremove
21215         * Fires before a child is removed, return false to cancel the remove.
21216         * @param {Tree} tree The owner tree
21217         * @param {Node} this This node
21218         * @param {Node} node The child node to be removed
21219         */
21220        "beforeremove" : true,
21221        /**
21222         * @event beforemove
21223         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21224         * @param {Tree} tree The owner tree
21225         * @param {Node} this This node
21226         * @param {Node} oldParent The parent of this node
21227         * @param {Node} newParent The new parent this node is moving to
21228         * @param {Number} index The index it is being moved to
21229         */
21230        "beforemove" : true,
21231        /**
21232         * @event beforeinsert
21233         * Fires before a new child is inserted, return false to cancel the insert.
21234         * @param {Tree} tree The owner tree
21235         * @param {Node} this This node
21236         * @param {Node} node The child node to be inserted
21237         * @param {Node} refNode The child node the node is being inserted before
21238         */
21239        "beforeinsert" : true
21240    });
21241     this.listeners = this.attributes.listeners;
21242     Roo.data.Node.superclass.constructor.call(this);
21243 };
21244
21245 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21246     fireEvent : function(evtName){
21247         // first do standard event for this node
21248         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21249             return false;
21250         }
21251         // then bubble it up to the tree if the event wasn't cancelled
21252         var ot = this.getOwnerTree();
21253         if(ot){
21254             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21255                 return false;
21256             }
21257         }
21258         return true;
21259     },
21260
21261     /**
21262      * Returns true if this node is a leaf
21263      * @return {Boolean}
21264      */
21265     isLeaf : function(){
21266         return this.leaf === true;
21267     },
21268
21269     // private
21270     setFirstChild : function(node){
21271         this.firstChild = node;
21272     },
21273
21274     //private
21275     setLastChild : function(node){
21276         this.lastChild = node;
21277     },
21278
21279
21280     /**
21281      * Returns true if this node is the last child of its parent
21282      * @return {Boolean}
21283      */
21284     isLast : function(){
21285        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21286     },
21287
21288     /**
21289      * Returns true if this node is the first child of its parent
21290      * @return {Boolean}
21291      */
21292     isFirst : function(){
21293        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21294     },
21295
21296     hasChildNodes : function(){
21297         return !this.isLeaf() && this.childNodes.length > 0;
21298     },
21299
21300     /**
21301      * Insert node(s) as the last child node of this node.
21302      * @param {Node/Array} node The node or Array of nodes to append
21303      * @return {Node} The appended node if single append, or null if an array was passed
21304      */
21305     appendChild : function(node){
21306         var multi = false;
21307         if(node instanceof Array){
21308             multi = node;
21309         }else if(arguments.length > 1){
21310             multi = arguments;
21311         }
21312         // if passed an array or multiple args do them one by one
21313         if(multi){
21314             for(var i = 0, len = multi.length; i < len; i++) {
21315                 this.appendChild(multi[i]);
21316             }
21317         }else{
21318             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21319                 return false;
21320             }
21321             var index = this.childNodes.length;
21322             var oldParent = node.parentNode;
21323             // it's a move, make sure we move it cleanly
21324             if(oldParent){
21325                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21326                     return false;
21327                 }
21328                 oldParent.removeChild(node);
21329             }
21330             index = this.childNodes.length;
21331             if(index == 0){
21332                 this.setFirstChild(node);
21333             }
21334             this.childNodes.push(node);
21335             node.parentNode = this;
21336             var ps = this.childNodes[index-1];
21337             if(ps){
21338                 node.previousSibling = ps;
21339                 ps.nextSibling = node;
21340             }else{
21341                 node.previousSibling = null;
21342             }
21343             node.nextSibling = null;
21344             this.setLastChild(node);
21345             node.setOwnerTree(this.getOwnerTree());
21346             this.fireEvent("append", this.ownerTree, this, node, index);
21347             if(oldParent){
21348                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21349             }
21350             return node;
21351         }
21352     },
21353
21354     /**
21355      * Removes a child node from this node.
21356      * @param {Node} node The node to remove
21357      * @return {Node} The removed node
21358      */
21359     removeChild : function(node){
21360         var index = this.childNodes.indexOf(node);
21361         if(index == -1){
21362             return false;
21363         }
21364         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21365             return false;
21366         }
21367
21368         // remove it from childNodes collection
21369         this.childNodes.splice(index, 1);
21370
21371         // update siblings
21372         if(node.previousSibling){
21373             node.previousSibling.nextSibling = node.nextSibling;
21374         }
21375         if(node.nextSibling){
21376             node.nextSibling.previousSibling = node.previousSibling;
21377         }
21378
21379         // update child refs
21380         if(this.firstChild == node){
21381             this.setFirstChild(node.nextSibling);
21382         }
21383         if(this.lastChild == node){
21384             this.setLastChild(node.previousSibling);
21385         }
21386
21387         node.setOwnerTree(null);
21388         // clear any references from the node
21389         node.parentNode = null;
21390         node.previousSibling = null;
21391         node.nextSibling = null;
21392         this.fireEvent("remove", this.ownerTree, this, node);
21393         return node;
21394     },
21395
21396     /**
21397      * Inserts the first node before the second node in this nodes childNodes collection.
21398      * @param {Node} node The node to insert
21399      * @param {Node} refNode The node to insert before (if null the node is appended)
21400      * @return {Node} The inserted node
21401      */
21402     insertBefore : function(node, refNode){
21403         if(!refNode){ // like standard Dom, refNode can be null for append
21404             return this.appendChild(node);
21405         }
21406         // nothing to do
21407         if(node == refNode){
21408             return false;
21409         }
21410
21411         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21412             return false;
21413         }
21414         var index = this.childNodes.indexOf(refNode);
21415         var oldParent = node.parentNode;
21416         var refIndex = index;
21417
21418         // when moving internally, indexes will change after remove
21419         if(oldParent == this && this.childNodes.indexOf(node) < index){
21420             refIndex--;
21421         }
21422
21423         // it's a move, make sure we move it cleanly
21424         if(oldParent){
21425             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21426                 return false;
21427             }
21428             oldParent.removeChild(node);
21429         }
21430         if(refIndex == 0){
21431             this.setFirstChild(node);
21432         }
21433         this.childNodes.splice(refIndex, 0, node);
21434         node.parentNode = this;
21435         var ps = this.childNodes[refIndex-1];
21436         if(ps){
21437             node.previousSibling = ps;
21438             ps.nextSibling = node;
21439         }else{
21440             node.previousSibling = null;
21441         }
21442         node.nextSibling = refNode;
21443         refNode.previousSibling = node;
21444         node.setOwnerTree(this.getOwnerTree());
21445         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21446         if(oldParent){
21447             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21448         }
21449         return node;
21450     },
21451
21452     /**
21453      * Returns the child node at the specified index.
21454      * @param {Number} index
21455      * @return {Node}
21456      */
21457     item : function(index){
21458         return this.childNodes[index];
21459     },
21460
21461     /**
21462      * Replaces one child node in this node with another.
21463      * @param {Node} newChild The replacement node
21464      * @param {Node} oldChild The node to replace
21465      * @return {Node} The replaced node
21466      */
21467     replaceChild : function(newChild, oldChild){
21468         this.insertBefore(newChild, oldChild);
21469         this.removeChild(oldChild);
21470         return oldChild;
21471     },
21472
21473     /**
21474      * Returns the index of a child node
21475      * @param {Node} node
21476      * @return {Number} The index of the node or -1 if it was not found
21477      */
21478     indexOf : function(child){
21479         return this.childNodes.indexOf(child);
21480     },
21481
21482     /**
21483      * Returns the tree this node is in.
21484      * @return {Tree}
21485      */
21486     getOwnerTree : function(){
21487         // if it doesn't have one, look for one
21488         if(!this.ownerTree){
21489             var p = this;
21490             while(p){
21491                 if(p.ownerTree){
21492                     this.ownerTree = p.ownerTree;
21493                     break;
21494                 }
21495                 p = p.parentNode;
21496             }
21497         }
21498         return this.ownerTree;
21499     },
21500
21501     /**
21502      * Returns depth of this node (the root node has a depth of 0)
21503      * @return {Number}
21504      */
21505     getDepth : function(){
21506         var depth = 0;
21507         var p = this;
21508         while(p.parentNode){
21509             ++depth;
21510             p = p.parentNode;
21511         }
21512         return depth;
21513     },
21514
21515     // private
21516     setOwnerTree : function(tree){
21517         // if it's move, we need to update everyone
21518         if(tree != this.ownerTree){
21519             if(this.ownerTree){
21520                 this.ownerTree.unregisterNode(this);
21521             }
21522             this.ownerTree = tree;
21523             var cs = this.childNodes;
21524             for(var i = 0, len = cs.length; i < len; i++) {
21525                 cs[i].setOwnerTree(tree);
21526             }
21527             if(tree){
21528                 tree.registerNode(this);
21529             }
21530         }
21531     },
21532
21533     /**
21534      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21535      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21536      * @return {String} The path
21537      */
21538     getPath : function(attr){
21539         attr = attr || "id";
21540         var p = this.parentNode;
21541         var b = [this.attributes[attr]];
21542         while(p){
21543             b.unshift(p.attributes[attr]);
21544             p = p.parentNode;
21545         }
21546         var sep = this.getOwnerTree().pathSeparator;
21547         return sep + b.join(sep);
21548     },
21549
21550     /**
21551      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21552      * function call will be the scope provided or the current node. The arguments to the function
21553      * will be the args provided or the current node. If the function returns false at any point,
21554      * the bubble is stopped.
21555      * @param {Function} fn The function to call
21556      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21557      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21558      */
21559     bubble : function(fn, scope, args){
21560         var p = this;
21561         while(p){
21562             if(fn.call(scope || p, args || p) === false){
21563                 break;
21564             }
21565             p = p.parentNode;
21566         }
21567     },
21568
21569     /**
21570      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21571      * function call will be the scope provided or the current node. The arguments to the function
21572      * will be the args provided or the current node. If the function returns false at any point,
21573      * the cascade is stopped on that branch.
21574      * @param {Function} fn The function to call
21575      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21576      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21577      */
21578     cascade : function(fn, scope, args){
21579         if(fn.call(scope || this, args || this) !== false){
21580             var cs = this.childNodes;
21581             for(var i = 0, len = cs.length; i < len; i++) {
21582                 cs[i].cascade(fn, scope, args);
21583             }
21584         }
21585     },
21586
21587     /**
21588      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21589      * function call will be the scope provided or the current node. The arguments to the function
21590      * will be the args provided or the current node. If the function returns false at any point,
21591      * the iteration stops.
21592      * @param {Function} fn The function to call
21593      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21594      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21595      */
21596     eachChild : function(fn, scope, args){
21597         var cs = this.childNodes;
21598         for(var i = 0, len = cs.length; i < len; i++) {
21599                 if(fn.call(scope || this, args || cs[i]) === false){
21600                     break;
21601                 }
21602         }
21603     },
21604
21605     /**
21606      * Finds the first child that has the attribute with the specified value.
21607      * @param {String} attribute The attribute name
21608      * @param {Mixed} value The value to search for
21609      * @return {Node} The found child or null if none was found
21610      */
21611     findChild : function(attribute, value){
21612         var cs = this.childNodes;
21613         for(var i = 0, len = cs.length; i < len; i++) {
21614                 if(cs[i].attributes[attribute] == value){
21615                     return cs[i];
21616                 }
21617         }
21618         return null;
21619     },
21620
21621     /**
21622      * Finds the first child by a custom function. The child matches if the function passed
21623      * returns true.
21624      * @param {Function} fn
21625      * @param {Object} scope (optional)
21626      * @return {Node} The found child or null if none was found
21627      */
21628     findChildBy : function(fn, scope){
21629         var cs = this.childNodes;
21630         for(var i = 0, len = cs.length; i < len; i++) {
21631                 if(fn.call(scope||cs[i], cs[i]) === true){
21632                     return cs[i];
21633                 }
21634         }
21635         return null;
21636     },
21637
21638     /**
21639      * Sorts this nodes children using the supplied sort function
21640      * @param {Function} fn
21641      * @param {Object} scope (optional)
21642      */
21643     sort : function(fn, scope){
21644         var cs = this.childNodes;
21645         var len = cs.length;
21646         if(len > 0){
21647             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21648             cs.sort(sortFn);
21649             for(var i = 0; i < len; i++){
21650                 var n = cs[i];
21651                 n.previousSibling = cs[i-1];
21652                 n.nextSibling = cs[i+1];
21653                 if(i == 0){
21654                     this.setFirstChild(n);
21655                 }
21656                 if(i == len-1){
21657                     this.setLastChild(n);
21658                 }
21659             }
21660         }
21661     },
21662
21663     /**
21664      * Returns true if this node is an ancestor (at any point) of the passed node.
21665      * @param {Node} node
21666      * @return {Boolean}
21667      */
21668     contains : function(node){
21669         return node.isAncestor(this);
21670     },
21671
21672     /**
21673      * Returns true if the passed node is an ancestor (at any point) of this node.
21674      * @param {Node} node
21675      * @return {Boolean}
21676      */
21677     isAncestor : function(node){
21678         var p = this.parentNode;
21679         while(p){
21680             if(p == node){
21681                 return true;
21682             }
21683             p = p.parentNode;
21684         }
21685         return false;
21686     },
21687
21688     toString : function(){
21689         return "[Node"+(this.id?" "+this.id:"")+"]";
21690     }
21691 });/*
21692  * Based on:
21693  * Ext JS Library 1.1.1
21694  * Copyright(c) 2006-2007, Ext JS, LLC.
21695  *
21696  * Originally Released Under LGPL - original licence link has changed is not relivant.
21697  *
21698  * Fork - LGPL
21699  * <script type="text/javascript">
21700  */
21701  
21702
21703 /**
21704  * @class Roo.ComponentMgr
21705  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21706  * @singleton
21707  */
21708 Roo.ComponentMgr = function(){
21709     var all = new Roo.util.MixedCollection();
21710
21711     return {
21712         /**
21713          * Registers a component.
21714          * @param {Roo.Component} c The component
21715          */
21716         register : function(c){
21717             all.add(c);
21718         },
21719
21720         /**
21721          * Unregisters a component.
21722          * @param {Roo.Component} c The component
21723          */
21724         unregister : function(c){
21725             all.remove(c);
21726         },
21727
21728         /**
21729          * Returns a component by id
21730          * @param {String} id The component id
21731          */
21732         get : function(id){
21733             return all.get(id);
21734         },
21735
21736         /**
21737          * Registers a function that will be called when a specified component is added to ComponentMgr
21738          * @param {String} id The component id
21739          * @param {Funtction} fn The callback function
21740          * @param {Object} scope The scope of the callback
21741          */
21742         onAvailable : function(id, fn, scope){
21743             all.on("add", function(index, o){
21744                 if(o.id == id){
21745                     fn.call(scope || o, o);
21746                     all.un("add", fn, scope);
21747                 }
21748             });
21749         }
21750     };
21751 }();/*
21752  * Based on:
21753  * Ext JS Library 1.1.1
21754  * Copyright(c) 2006-2007, Ext JS, LLC.
21755  *
21756  * Originally Released Under LGPL - original licence link has changed is not relivant.
21757  *
21758  * Fork - LGPL
21759  * <script type="text/javascript">
21760  */
21761  
21762 /**
21763  * @class Roo.Component
21764  * @extends Roo.util.Observable
21765  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21766  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21767  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21768  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21769  * All visual components (widgets) that require rendering into a layout should subclass Component.
21770  * @constructor
21771  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21772  * 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
21773  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21774  */
21775 Roo.Component = function(config){
21776     config = config || {};
21777     if(config.tagName || config.dom || typeof config == "string"){ // element object
21778         config = {el: config, id: config.id || config};
21779     }
21780     this.initialConfig = config;
21781
21782     Roo.apply(this, config);
21783     this.addEvents({
21784         /**
21785          * @event disable
21786          * Fires after the component is disabled.
21787              * @param {Roo.Component} this
21788              */
21789         disable : true,
21790         /**
21791          * @event enable
21792          * Fires after the component is enabled.
21793              * @param {Roo.Component} this
21794              */
21795         enable : true,
21796         /**
21797          * @event beforeshow
21798          * Fires before the component is shown.  Return false to stop the show.
21799              * @param {Roo.Component} this
21800              */
21801         beforeshow : true,
21802         /**
21803          * @event show
21804          * Fires after the component is shown.
21805              * @param {Roo.Component} this
21806              */
21807         show : true,
21808         /**
21809          * @event beforehide
21810          * Fires before the component is hidden. Return false to stop the hide.
21811              * @param {Roo.Component} this
21812              */
21813         beforehide : true,
21814         /**
21815          * @event hide
21816          * Fires after the component is hidden.
21817              * @param {Roo.Component} this
21818              */
21819         hide : true,
21820         /**
21821          * @event beforerender
21822          * Fires before the component is rendered. Return false to stop the render.
21823              * @param {Roo.Component} this
21824              */
21825         beforerender : true,
21826         /**
21827          * @event render
21828          * Fires after the component is rendered.
21829              * @param {Roo.Component} this
21830              */
21831         render : true,
21832         /**
21833          * @event beforedestroy
21834          * Fires before the component is destroyed. Return false to stop the destroy.
21835              * @param {Roo.Component} this
21836              */
21837         beforedestroy : true,
21838         /**
21839          * @event destroy
21840          * Fires after the component is destroyed.
21841              * @param {Roo.Component} this
21842              */
21843         destroy : true
21844     });
21845     if(!this.id){
21846         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21847     }
21848     Roo.ComponentMgr.register(this);
21849     Roo.Component.superclass.constructor.call(this);
21850     this.initComponent();
21851     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21852         this.render(this.renderTo);
21853         delete this.renderTo;
21854     }
21855 };
21856
21857 /** @private */
21858 Roo.Component.AUTO_ID = 1000;
21859
21860 Roo.extend(Roo.Component, Roo.util.Observable, {
21861     /**
21862      * @scope Roo.Component.prototype
21863      * @type {Boolean}
21864      * true if this component is hidden. Read-only.
21865      */
21866     hidden : false,
21867     /**
21868      * @type {Boolean}
21869      * true if this component is disabled. Read-only.
21870      */
21871     disabled : false,
21872     /**
21873      * @type {Boolean}
21874      * true if this component has been rendered. Read-only.
21875      */
21876     rendered : false,
21877     
21878     /** @cfg {String} disableClass
21879      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21880      */
21881     disabledClass : "x-item-disabled",
21882         /** @cfg {Boolean} allowDomMove
21883          * Whether the component can move the Dom node when rendering (defaults to true).
21884          */
21885     allowDomMove : true,
21886     /** @cfg {String} hideMode
21887      * How this component should hidden. Supported values are
21888      * "visibility" (css visibility), "offsets" (negative offset position) and
21889      * "display" (css display) - defaults to "display".
21890      */
21891     hideMode: 'display',
21892
21893     /** @private */
21894     ctype : "Roo.Component",
21895
21896     /**
21897      * @cfg {String} actionMode 
21898      * which property holds the element that used for  hide() / show() / disable() / enable()
21899      * default is 'el' 
21900      */
21901     actionMode : "el",
21902
21903     /** @private */
21904     getActionEl : function(){
21905         return this[this.actionMode];
21906     },
21907
21908     initComponent : Roo.emptyFn,
21909     /**
21910      * If this is a lazy rendering component, render it to its container element.
21911      * @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.
21912      */
21913     render : function(container, position){
21914         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21915             if(!container && this.el){
21916                 this.el = Roo.get(this.el);
21917                 container = this.el.dom.parentNode;
21918                 this.allowDomMove = false;
21919             }
21920             this.container = Roo.get(container);
21921             this.rendered = true;
21922             if(position !== undefined){
21923                 if(typeof position == 'number'){
21924                     position = this.container.dom.childNodes[position];
21925                 }else{
21926                     position = Roo.getDom(position);
21927                 }
21928             }
21929             this.onRender(this.container, position || null);
21930             if(this.cls){
21931                 this.el.addClass(this.cls);
21932                 delete this.cls;
21933             }
21934             if(this.style){
21935                 this.el.applyStyles(this.style);
21936                 delete this.style;
21937             }
21938             this.fireEvent("render", this);
21939             this.afterRender(this.container);
21940             if(this.hidden){
21941                 this.hide();
21942             }
21943             if(this.disabled){
21944                 this.disable();
21945             }
21946         }
21947         return this;
21948     },
21949
21950     /** @private */
21951     // default function is not really useful
21952     onRender : function(ct, position){
21953         if(this.el){
21954             this.el = Roo.get(this.el);
21955             if(this.allowDomMove !== false){
21956                 ct.dom.insertBefore(this.el.dom, position);
21957             }
21958         }
21959     },
21960
21961     /** @private */
21962     getAutoCreate : function(){
21963         var cfg = typeof this.autoCreate == "object" ?
21964                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21965         if(this.id && !cfg.id){
21966             cfg.id = this.id;
21967         }
21968         return cfg;
21969     },
21970
21971     /** @private */
21972     afterRender : Roo.emptyFn,
21973
21974     /**
21975      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21976      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21977      */
21978     destroy : function(){
21979         if(this.fireEvent("beforedestroy", this) !== false){
21980             this.purgeListeners();
21981             this.beforeDestroy();
21982             if(this.rendered){
21983                 this.el.removeAllListeners();
21984                 this.el.remove();
21985                 if(this.actionMode == "container"){
21986                     this.container.remove();
21987                 }
21988             }
21989             this.onDestroy();
21990             Roo.ComponentMgr.unregister(this);
21991             this.fireEvent("destroy", this);
21992         }
21993     },
21994
21995         /** @private */
21996     beforeDestroy : function(){
21997
21998     },
21999
22000         /** @private */
22001         onDestroy : function(){
22002
22003     },
22004
22005     /**
22006      * Returns the underlying {@link Roo.Element}.
22007      * @return {Roo.Element} The element
22008      */
22009     getEl : function(){
22010         return this.el;
22011     },
22012
22013     /**
22014      * Returns the id of this component.
22015      * @return {String}
22016      */
22017     getId : function(){
22018         return this.id;
22019     },
22020
22021     /**
22022      * Try to focus this component.
22023      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22024      * @return {Roo.Component} this
22025      */
22026     focus : function(selectText){
22027         if(this.rendered){
22028             this.el.focus();
22029             if(selectText === true){
22030                 this.el.dom.select();
22031             }
22032         }
22033         return this;
22034     },
22035
22036     /** @private */
22037     blur : function(){
22038         if(this.rendered){
22039             this.el.blur();
22040         }
22041         return this;
22042     },
22043
22044     /**
22045      * Disable this component.
22046      * @return {Roo.Component} this
22047      */
22048     disable : function(){
22049         if(this.rendered){
22050             this.onDisable();
22051         }
22052         this.disabled = true;
22053         this.fireEvent("disable", this);
22054         return this;
22055     },
22056
22057         // private
22058     onDisable : function(){
22059         this.getActionEl().addClass(this.disabledClass);
22060         this.el.dom.disabled = true;
22061     },
22062
22063     /**
22064      * Enable this component.
22065      * @return {Roo.Component} this
22066      */
22067     enable : function(){
22068         if(this.rendered){
22069             this.onEnable();
22070         }
22071         this.disabled = false;
22072         this.fireEvent("enable", this);
22073         return this;
22074     },
22075
22076         // private
22077     onEnable : function(){
22078         this.getActionEl().removeClass(this.disabledClass);
22079         this.el.dom.disabled = false;
22080     },
22081
22082     /**
22083      * Convenience function for setting disabled/enabled by boolean.
22084      * @param {Boolean} disabled
22085      */
22086     setDisabled : function(disabled){
22087         this[disabled ? "disable" : "enable"]();
22088     },
22089
22090     /**
22091      * Show this component.
22092      * @return {Roo.Component} this
22093      */
22094     show: function(){
22095         if(this.fireEvent("beforeshow", this) !== false){
22096             this.hidden = false;
22097             if(this.rendered){
22098                 this.onShow();
22099             }
22100             this.fireEvent("show", this);
22101         }
22102         return this;
22103     },
22104
22105     // private
22106     onShow : function(){
22107         var ae = this.getActionEl();
22108         if(this.hideMode == 'visibility'){
22109             ae.dom.style.visibility = "visible";
22110         }else if(this.hideMode == 'offsets'){
22111             ae.removeClass('x-hidden');
22112         }else{
22113             ae.dom.style.display = "";
22114         }
22115     },
22116
22117     /**
22118      * Hide this component.
22119      * @return {Roo.Component} this
22120      */
22121     hide: function(){
22122         if(this.fireEvent("beforehide", this) !== false){
22123             this.hidden = true;
22124             if(this.rendered){
22125                 this.onHide();
22126             }
22127             this.fireEvent("hide", this);
22128         }
22129         return this;
22130     },
22131
22132     // private
22133     onHide : function(){
22134         var ae = this.getActionEl();
22135         if(this.hideMode == 'visibility'){
22136             ae.dom.style.visibility = "hidden";
22137         }else if(this.hideMode == 'offsets'){
22138             ae.addClass('x-hidden');
22139         }else{
22140             ae.dom.style.display = "none";
22141         }
22142     },
22143
22144     /**
22145      * Convenience function to hide or show this component by boolean.
22146      * @param {Boolean} visible True to show, false to hide
22147      * @return {Roo.Component} this
22148      */
22149     setVisible: function(visible){
22150         if(visible) {
22151             this.show();
22152         }else{
22153             this.hide();
22154         }
22155         return this;
22156     },
22157
22158     /**
22159      * Returns true if this component is visible.
22160      */
22161     isVisible : function(){
22162         return this.getActionEl().isVisible();
22163     },
22164
22165     cloneConfig : function(overrides){
22166         overrides = overrides || {};
22167         var id = overrides.id || Roo.id();
22168         var cfg = Roo.applyIf(overrides, this.initialConfig);
22169         cfg.id = id; // prevent dup id
22170         return new this.constructor(cfg);
22171     }
22172 });/*
22173  * Based on:
22174  * Ext JS Library 1.1.1
22175  * Copyright(c) 2006-2007, Ext JS, LLC.
22176  *
22177  * Originally Released Under LGPL - original licence link has changed is not relivant.
22178  *
22179  * Fork - LGPL
22180  * <script type="text/javascript">
22181  */
22182  (function(){ 
22183 /**
22184  * @class Roo.Layer
22185  * @extends Roo.Element
22186  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22187  * automatic maintaining of shadow/shim positions.
22188  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22189  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22190  * you can pass a string with a CSS class name. False turns off the shadow.
22191  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22192  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22193  * @cfg {String} cls CSS class to add to the element
22194  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22195  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22196  * @constructor
22197  * @param {Object} config An object with config options.
22198  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22199  */
22200
22201 Roo.Layer = function(config, existingEl){
22202     config = config || {};
22203     var dh = Roo.DomHelper;
22204     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22205     if(existingEl){
22206         this.dom = Roo.getDom(existingEl);
22207     }
22208     if(!this.dom){
22209         var o = config.dh || {tag: "div", cls: "x-layer"};
22210         this.dom = dh.append(pel, o);
22211     }
22212     if(config.cls){
22213         this.addClass(config.cls);
22214     }
22215     this.constrain = config.constrain !== false;
22216     this.visibilityMode = Roo.Element.VISIBILITY;
22217     if(config.id){
22218         this.id = this.dom.id = config.id;
22219     }else{
22220         this.id = Roo.id(this.dom);
22221     }
22222     this.zindex = config.zindex || this.getZIndex();
22223     this.position("absolute", this.zindex);
22224     if(config.shadow){
22225         this.shadowOffset = config.shadowOffset || 4;
22226         this.shadow = new Roo.Shadow({
22227             offset : this.shadowOffset,
22228             mode : config.shadow
22229         });
22230     }else{
22231         this.shadowOffset = 0;
22232     }
22233     this.useShim = config.shim !== false && Roo.useShims;
22234     this.useDisplay = config.useDisplay;
22235     this.hide();
22236 };
22237
22238 var supr = Roo.Element.prototype;
22239
22240 // shims are shared among layer to keep from having 100 iframes
22241 var shims = [];
22242
22243 Roo.extend(Roo.Layer, Roo.Element, {
22244
22245     getZIndex : function(){
22246         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22247     },
22248
22249     getShim : function(){
22250         if(!this.useShim){
22251             return null;
22252         }
22253         if(this.shim){
22254             return this.shim;
22255         }
22256         var shim = shims.shift();
22257         if(!shim){
22258             shim = this.createShim();
22259             shim.enableDisplayMode('block');
22260             shim.dom.style.display = 'none';
22261             shim.dom.style.visibility = 'visible';
22262         }
22263         var pn = this.dom.parentNode;
22264         if(shim.dom.parentNode != pn){
22265             pn.insertBefore(shim.dom, this.dom);
22266         }
22267         shim.setStyle('z-index', this.getZIndex()-2);
22268         this.shim = shim;
22269         return shim;
22270     },
22271
22272     hideShim : function(){
22273         if(this.shim){
22274             this.shim.setDisplayed(false);
22275             shims.push(this.shim);
22276             delete this.shim;
22277         }
22278     },
22279
22280     disableShadow : function(){
22281         if(this.shadow){
22282             this.shadowDisabled = true;
22283             this.shadow.hide();
22284             this.lastShadowOffset = this.shadowOffset;
22285             this.shadowOffset = 0;
22286         }
22287     },
22288
22289     enableShadow : function(show){
22290         if(this.shadow){
22291             this.shadowDisabled = false;
22292             this.shadowOffset = this.lastShadowOffset;
22293             delete this.lastShadowOffset;
22294             if(show){
22295                 this.sync(true);
22296             }
22297         }
22298     },
22299
22300     // private
22301     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22302     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22303     sync : function(doShow){
22304         var sw = this.shadow;
22305         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22306             var sh = this.getShim();
22307
22308             var w = this.getWidth(),
22309                 h = this.getHeight();
22310
22311             var l = this.getLeft(true),
22312                 t = this.getTop(true);
22313
22314             if(sw && !this.shadowDisabled){
22315                 if(doShow && !sw.isVisible()){
22316                     sw.show(this);
22317                 }else{
22318                     sw.realign(l, t, w, h);
22319                 }
22320                 if(sh){
22321                     if(doShow){
22322                        sh.show();
22323                     }
22324                     // fit the shim behind the shadow, so it is shimmed too
22325                     var a = sw.adjusts, s = sh.dom.style;
22326                     s.left = (Math.min(l, l+a.l))+"px";
22327                     s.top = (Math.min(t, t+a.t))+"px";
22328                     s.width = (w+a.w)+"px";
22329                     s.height = (h+a.h)+"px";
22330                 }
22331             }else if(sh){
22332                 if(doShow){
22333                    sh.show();
22334                 }
22335                 sh.setSize(w, h);
22336                 sh.setLeftTop(l, t);
22337             }
22338             
22339         }
22340     },
22341
22342     // private
22343     destroy : function(){
22344         this.hideShim();
22345         if(this.shadow){
22346             this.shadow.hide();
22347         }
22348         this.removeAllListeners();
22349         var pn = this.dom.parentNode;
22350         if(pn){
22351             pn.removeChild(this.dom);
22352         }
22353         Roo.Element.uncache(this.id);
22354     },
22355
22356     remove : function(){
22357         this.destroy();
22358     },
22359
22360     // private
22361     beginUpdate : function(){
22362         this.updating = true;
22363     },
22364
22365     // private
22366     endUpdate : function(){
22367         this.updating = false;
22368         this.sync(true);
22369     },
22370
22371     // private
22372     hideUnders : function(negOffset){
22373         if(this.shadow){
22374             this.shadow.hide();
22375         }
22376         this.hideShim();
22377     },
22378
22379     // private
22380     constrainXY : function(){
22381         if(this.constrain){
22382             var vw = Roo.lib.Dom.getViewWidth(),
22383                 vh = Roo.lib.Dom.getViewHeight();
22384             var s = Roo.get(document).getScroll();
22385
22386             var xy = this.getXY();
22387             var x = xy[0], y = xy[1];   
22388             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22389             // only move it if it needs it
22390             var moved = false;
22391             // first validate right/bottom
22392             if((x + w) > vw+s.left){
22393                 x = vw - w - this.shadowOffset;
22394                 moved = true;
22395             }
22396             if((y + h) > vh+s.top){
22397                 y = vh - h - this.shadowOffset;
22398                 moved = true;
22399             }
22400             // then make sure top/left isn't negative
22401             if(x < s.left){
22402                 x = s.left;
22403                 moved = true;
22404             }
22405             if(y < s.top){
22406                 y = s.top;
22407                 moved = true;
22408             }
22409             if(moved){
22410                 if(this.avoidY){
22411                     var ay = this.avoidY;
22412                     if(y <= ay && (y+h) >= ay){
22413                         y = ay-h-5;   
22414                     }
22415                 }
22416                 xy = [x, y];
22417                 this.storeXY(xy);
22418                 supr.setXY.call(this, xy);
22419                 this.sync();
22420             }
22421         }
22422     },
22423
22424     isVisible : function(){
22425         return this.visible;    
22426     },
22427
22428     // private
22429     showAction : function(){
22430         this.visible = true; // track visibility to prevent getStyle calls
22431         if(this.useDisplay === true){
22432             this.setDisplayed("");
22433         }else if(this.lastXY){
22434             supr.setXY.call(this, this.lastXY);
22435         }else if(this.lastLT){
22436             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22437         }
22438     },
22439
22440     // private
22441     hideAction : function(){
22442         this.visible = false;
22443         if(this.useDisplay === true){
22444             this.setDisplayed(false);
22445         }else{
22446             this.setLeftTop(-10000,-10000);
22447         }
22448     },
22449
22450     // overridden Element method
22451     setVisible : function(v, a, d, c, e){
22452         if(v){
22453             this.showAction();
22454         }
22455         if(a && v){
22456             var cb = function(){
22457                 this.sync(true);
22458                 if(c){
22459                     c();
22460                 }
22461             }.createDelegate(this);
22462             supr.setVisible.call(this, true, true, d, cb, e);
22463         }else{
22464             if(!v){
22465                 this.hideUnders(true);
22466             }
22467             var cb = c;
22468             if(a){
22469                 cb = function(){
22470                     this.hideAction();
22471                     if(c){
22472                         c();
22473                     }
22474                 }.createDelegate(this);
22475             }
22476             supr.setVisible.call(this, v, a, d, cb, e);
22477             if(v){
22478                 this.sync(true);
22479             }else if(!a){
22480                 this.hideAction();
22481             }
22482         }
22483     },
22484
22485     storeXY : function(xy){
22486         delete this.lastLT;
22487         this.lastXY = xy;
22488     },
22489
22490     storeLeftTop : function(left, top){
22491         delete this.lastXY;
22492         this.lastLT = [left, top];
22493     },
22494
22495     // private
22496     beforeFx : function(){
22497         this.beforeAction();
22498         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22499     },
22500
22501     // private
22502     afterFx : function(){
22503         Roo.Layer.superclass.afterFx.apply(this, arguments);
22504         this.sync(this.isVisible());
22505     },
22506
22507     // private
22508     beforeAction : function(){
22509         if(!this.updating && this.shadow){
22510             this.shadow.hide();
22511         }
22512     },
22513
22514     // overridden Element method
22515     setLeft : function(left){
22516         this.storeLeftTop(left, this.getTop(true));
22517         supr.setLeft.apply(this, arguments);
22518         this.sync();
22519     },
22520
22521     setTop : function(top){
22522         this.storeLeftTop(this.getLeft(true), top);
22523         supr.setTop.apply(this, arguments);
22524         this.sync();
22525     },
22526
22527     setLeftTop : function(left, top){
22528         this.storeLeftTop(left, top);
22529         supr.setLeftTop.apply(this, arguments);
22530         this.sync();
22531     },
22532
22533     setXY : function(xy, a, d, c, e){
22534         this.fixDisplay();
22535         this.beforeAction();
22536         this.storeXY(xy);
22537         var cb = this.createCB(c);
22538         supr.setXY.call(this, xy, a, d, cb, e);
22539         if(!a){
22540             cb();
22541         }
22542     },
22543
22544     // private
22545     createCB : function(c){
22546         var el = this;
22547         return function(){
22548             el.constrainXY();
22549             el.sync(true);
22550             if(c){
22551                 c();
22552             }
22553         };
22554     },
22555
22556     // overridden Element method
22557     setX : function(x, a, d, c, e){
22558         this.setXY([x, this.getY()], a, d, c, e);
22559     },
22560
22561     // overridden Element method
22562     setY : function(y, a, d, c, e){
22563         this.setXY([this.getX(), y], a, d, c, e);
22564     },
22565
22566     // overridden Element method
22567     setSize : function(w, h, a, d, c, e){
22568         this.beforeAction();
22569         var cb = this.createCB(c);
22570         supr.setSize.call(this, w, h, a, d, cb, e);
22571         if(!a){
22572             cb();
22573         }
22574     },
22575
22576     // overridden Element method
22577     setWidth : function(w, a, d, c, e){
22578         this.beforeAction();
22579         var cb = this.createCB(c);
22580         supr.setWidth.call(this, w, a, d, cb, e);
22581         if(!a){
22582             cb();
22583         }
22584     },
22585
22586     // overridden Element method
22587     setHeight : function(h, a, d, c, e){
22588         this.beforeAction();
22589         var cb = this.createCB(c);
22590         supr.setHeight.call(this, h, a, d, cb, e);
22591         if(!a){
22592             cb();
22593         }
22594     },
22595
22596     // overridden Element method
22597     setBounds : function(x, y, w, h, a, d, c, e){
22598         this.beforeAction();
22599         var cb = this.createCB(c);
22600         if(!a){
22601             this.storeXY([x, y]);
22602             supr.setXY.call(this, [x, y]);
22603             supr.setSize.call(this, w, h, a, d, cb, e);
22604             cb();
22605         }else{
22606             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22607         }
22608         return this;
22609     },
22610     
22611     /**
22612      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22613      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22614      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22615      * @param {Number} zindex The new z-index to set
22616      * @return {this} The Layer
22617      */
22618     setZIndex : function(zindex){
22619         this.zindex = zindex;
22620         this.setStyle("z-index", zindex + 2);
22621         if(this.shadow){
22622             this.shadow.setZIndex(zindex + 1);
22623         }
22624         if(this.shim){
22625             this.shim.setStyle("z-index", zindex);
22626         }
22627     }
22628 });
22629 })();/*
22630  * Based on:
22631  * Ext JS Library 1.1.1
22632  * Copyright(c) 2006-2007, Ext JS, LLC.
22633  *
22634  * Originally Released Under LGPL - original licence link has changed is not relivant.
22635  *
22636  * Fork - LGPL
22637  * <script type="text/javascript">
22638  */
22639
22640
22641 /**
22642  * @class Roo.Shadow
22643  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22644  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22645  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22646  * @constructor
22647  * Create a new Shadow
22648  * @param {Object} config The config object
22649  */
22650 Roo.Shadow = function(config){
22651     Roo.apply(this, config);
22652     if(typeof this.mode != "string"){
22653         this.mode = this.defaultMode;
22654     }
22655     var o = this.offset, a = {h: 0};
22656     var rad = Math.floor(this.offset/2);
22657     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22658         case "drop":
22659             a.w = 0;
22660             a.l = a.t = o;
22661             a.t -= 1;
22662             if(Roo.isIE){
22663                 a.l -= this.offset + rad;
22664                 a.t -= this.offset + rad;
22665                 a.w -= rad;
22666                 a.h -= rad;
22667                 a.t += 1;
22668             }
22669         break;
22670         case "sides":
22671             a.w = (o*2);
22672             a.l = -o;
22673             a.t = o-1;
22674             if(Roo.isIE){
22675                 a.l -= (this.offset - rad);
22676                 a.t -= this.offset + rad;
22677                 a.l += 1;
22678                 a.w -= (this.offset - rad)*2;
22679                 a.w -= rad + 1;
22680                 a.h -= 1;
22681             }
22682         break;
22683         case "frame":
22684             a.w = a.h = (o*2);
22685             a.l = a.t = -o;
22686             a.t += 1;
22687             a.h -= 2;
22688             if(Roo.isIE){
22689                 a.l -= (this.offset - rad);
22690                 a.t -= (this.offset - rad);
22691                 a.l += 1;
22692                 a.w -= (this.offset + rad + 1);
22693                 a.h -= (this.offset + rad);
22694                 a.h += 1;
22695             }
22696         break;
22697     };
22698
22699     this.adjusts = a;
22700 };
22701
22702 Roo.Shadow.prototype = {
22703     /**
22704      * @cfg {String} mode
22705      * The shadow display mode.  Supports the following options:<br />
22706      * sides: Shadow displays on both sides and bottom only<br />
22707      * frame: Shadow displays equally on all four sides<br />
22708      * drop: Traditional bottom-right drop shadow (default)
22709      */
22710     /**
22711      * @cfg {String} offset
22712      * The number of pixels to offset the shadow from the element (defaults to 4)
22713      */
22714     offset: 4,
22715
22716     // private
22717     defaultMode: "drop",
22718
22719     /**
22720      * Displays the shadow under the target element
22721      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22722      */
22723     show : function(target){
22724         target = Roo.get(target);
22725         if(!this.el){
22726             this.el = Roo.Shadow.Pool.pull();
22727             if(this.el.dom.nextSibling != target.dom){
22728                 this.el.insertBefore(target);
22729             }
22730         }
22731         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22732         if(Roo.isIE){
22733             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22734         }
22735         this.realign(
22736             target.getLeft(true),
22737             target.getTop(true),
22738             target.getWidth(),
22739             target.getHeight()
22740         );
22741         this.el.dom.style.display = "block";
22742     },
22743
22744     /**
22745      * Returns true if the shadow is visible, else false
22746      */
22747     isVisible : function(){
22748         return this.el ? true : false;  
22749     },
22750
22751     /**
22752      * Direct alignment when values are already available. Show must be called at least once before
22753      * calling this method to ensure it is initialized.
22754      * @param {Number} left The target element left position
22755      * @param {Number} top The target element top position
22756      * @param {Number} width The target element width
22757      * @param {Number} height The target element height
22758      */
22759     realign : function(l, t, w, h){
22760         if(!this.el){
22761             return;
22762         }
22763         var a = this.adjusts, d = this.el.dom, s = d.style;
22764         var iea = 0;
22765         s.left = (l+a.l)+"px";
22766         s.top = (t+a.t)+"px";
22767         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22768  
22769         if(s.width != sws || s.height != shs){
22770             s.width = sws;
22771             s.height = shs;
22772             if(!Roo.isIE){
22773                 var cn = d.childNodes;
22774                 var sww = Math.max(0, (sw-12))+"px";
22775                 cn[0].childNodes[1].style.width = sww;
22776                 cn[1].childNodes[1].style.width = sww;
22777                 cn[2].childNodes[1].style.width = sww;
22778                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22779             }
22780         }
22781     },
22782
22783     /**
22784      * Hides this shadow
22785      */
22786     hide : function(){
22787         if(this.el){
22788             this.el.dom.style.display = "none";
22789             Roo.Shadow.Pool.push(this.el);
22790             delete this.el;
22791         }
22792     },
22793
22794     /**
22795      * Adjust the z-index of this shadow
22796      * @param {Number} zindex The new z-index
22797      */
22798     setZIndex : function(z){
22799         this.zIndex = z;
22800         if(this.el){
22801             this.el.setStyle("z-index", z);
22802         }
22803     }
22804 };
22805
22806 // Private utility class that manages the internal Shadow cache
22807 Roo.Shadow.Pool = function(){
22808     var p = [];
22809     var markup = Roo.isIE ?
22810                  '<div class="x-ie-shadow"></div>' :
22811                  '<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>';
22812     return {
22813         pull : function(){
22814             var sh = p.shift();
22815             if(!sh){
22816                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22817                 sh.autoBoxAdjust = false;
22818             }
22819             return sh;
22820         },
22821
22822         push : function(sh){
22823             p.push(sh);
22824         }
22825     };
22826 }();/*
22827  * Based on:
22828  * Ext JS Library 1.1.1
22829  * Copyright(c) 2006-2007, Ext JS, LLC.
22830  *
22831  * Originally Released Under LGPL - original licence link has changed is not relivant.
22832  *
22833  * Fork - LGPL
22834  * <script type="text/javascript">
22835  */
22836
22837 /**
22838  * @class Roo.BoxComponent
22839  * @extends Roo.Component
22840  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22841  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22842  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22843  * layout containers.
22844  * @constructor
22845  * @param {Roo.Element/String/Object} config The configuration options.
22846  */
22847 Roo.BoxComponent = function(config){
22848     Roo.Component.call(this, config);
22849     this.addEvents({
22850         /**
22851          * @event resize
22852          * Fires after the component is resized.
22853              * @param {Roo.Component} this
22854              * @param {Number} adjWidth The box-adjusted width that was set
22855              * @param {Number} adjHeight The box-adjusted height that was set
22856              * @param {Number} rawWidth The width that was originally specified
22857              * @param {Number} rawHeight The height that was originally specified
22858              */
22859         resize : true,
22860         /**
22861          * @event move
22862          * Fires after the component is moved.
22863              * @param {Roo.Component} this
22864              * @param {Number} x The new x position
22865              * @param {Number} y The new y position
22866              */
22867         move : true
22868     });
22869 };
22870
22871 Roo.extend(Roo.BoxComponent, Roo.Component, {
22872     // private, set in afterRender to signify that the component has been rendered
22873     boxReady : false,
22874     // private, used to defer height settings to subclasses
22875     deferHeight: false,
22876     /** @cfg {Number} width
22877      * width (optional) size of component
22878      */
22879      /** @cfg {Number} height
22880      * height (optional) size of component
22881      */
22882      
22883     /**
22884      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22885      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22886      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22887      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22888      * @return {Roo.BoxComponent} this
22889      */
22890     setSize : function(w, h){
22891         // support for standard size objects
22892         if(typeof w == 'object'){
22893             h = w.height;
22894             w = w.width;
22895         }
22896         // not rendered
22897         if(!this.boxReady){
22898             this.width = w;
22899             this.height = h;
22900             return this;
22901         }
22902
22903         // prevent recalcs when not needed
22904         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22905             return this;
22906         }
22907         this.lastSize = {width: w, height: h};
22908
22909         var adj = this.adjustSize(w, h);
22910         var aw = adj.width, ah = adj.height;
22911         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22912             var rz = this.getResizeEl();
22913             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22914                 rz.setSize(aw, ah);
22915             }else if(!this.deferHeight && ah !== undefined){
22916                 rz.setHeight(ah);
22917             }else if(aw !== undefined){
22918                 rz.setWidth(aw);
22919             }
22920             this.onResize(aw, ah, w, h);
22921             this.fireEvent('resize', this, aw, ah, w, h);
22922         }
22923         return this;
22924     },
22925
22926     /**
22927      * Gets the current size of the component's underlying element.
22928      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22929      */
22930     getSize : function(){
22931         return this.el.getSize();
22932     },
22933
22934     /**
22935      * Gets the current XY position of the component's underlying element.
22936      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22937      * @return {Array} The XY position of the element (e.g., [100, 200])
22938      */
22939     getPosition : function(local){
22940         if(local === true){
22941             return [this.el.getLeft(true), this.el.getTop(true)];
22942         }
22943         return this.xy || this.el.getXY();
22944     },
22945
22946     /**
22947      * Gets the current box measurements of the component's underlying element.
22948      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22949      * @returns {Object} box An object in the format {x, y, width, height}
22950      */
22951     getBox : function(local){
22952         var s = this.el.getSize();
22953         if(local){
22954             s.x = this.el.getLeft(true);
22955             s.y = this.el.getTop(true);
22956         }else{
22957             var xy = this.xy || this.el.getXY();
22958             s.x = xy[0];
22959             s.y = xy[1];
22960         }
22961         return s;
22962     },
22963
22964     /**
22965      * Sets the current box measurements of the component's underlying element.
22966      * @param {Object} box An object in the format {x, y, width, height}
22967      * @returns {Roo.BoxComponent} this
22968      */
22969     updateBox : function(box){
22970         this.setSize(box.width, box.height);
22971         this.setPagePosition(box.x, box.y);
22972         return this;
22973     },
22974
22975     // protected
22976     getResizeEl : function(){
22977         return this.resizeEl || this.el;
22978     },
22979
22980     // protected
22981     getPositionEl : function(){
22982         return this.positionEl || this.el;
22983     },
22984
22985     /**
22986      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22987      * This method fires the move event.
22988      * @param {Number} left The new left
22989      * @param {Number} top The new top
22990      * @returns {Roo.BoxComponent} this
22991      */
22992     setPosition : function(x, y){
22993         this.x = x;
22994         this.y = y;
22995         if(!this.boxReady){
22996             return this;
22997         }
22998         var adj = this.adjustPosition(x, y);
22999         var ax = adj.x, ay = adj.y;
23000
23001         var el = this.getPositionEl();
23002         if(ax !== undefined || ay !== undefined){
23003             if(ax !== undefined && ay !== undefined){
23004                 el.setLeftTop(ax, ay);
23005             }else if(ax !== undefined){
23006                 el.setLeft(ax);
23007             }else if(ay !== undefined){
23008                 el.setTop(ay);
23009             }
23010             this.onPosition(ax, ay);
23011             this.fireEvent('move', this, ax, ay);
23012         }
23013         return this;
23014     },
23015
23016     /**
23017      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23018      * This method fires the move event.
23019      * @param {Number} x The new x position
23020      * @param {Number} y The new y position
23021      * @returns {Roo.BoxComponent} this
23022      */
23023     setPagePosition : function(x, y){
23024         this.pageX = x;
23025         this.pageY = y;
23026         if(!this.boxReady){
23027             return;
23028         }
23029         if(x === undefined || y === undefined){ // cannot translate undefined points
23030             return;
23031         }
23032         var p = this.el.translatePoints(x, y);
23033         this.setPosition(p.left, p.top);
23034         return this;
23035     },
23036
23037     // private
23038     onRender : function(ct, position){
23039         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23040         if(this.resizeEl){
23041             this.resizeEl = Roo.get(this.resizeEl);
23042         }
23043         if(this.positionEl){
23044             this.positionEl = Roo.get(this.positionEl);
23045         }
23046     },
23047
23048     // private
23049     afterRender : function(){
23050         Roo.BoxComponent.superclass.afterRender.call(this);
23051         this.boxReady = true;
23052         this.setSize(this.width, this.height);
23053         if(this.x || this.y){
23054             this.setPosition(this.x, this.y);
23055         }
23056         if(this.pageX || this.pageY){
23057             this.setPagePosition(this.pageX, this.pageY);
23058         }
23059     },
23060
23061     /**
23062      * Force the component's size to recalculate based on the underlying element's current height and width.
23063      * @returns {Roo.BoxComponent} this
23064      */
23065     syncSize : function(){
23066         delete this.lastSize;
23067         this.setSize(this.el.getWidth(), this.el.getHeight());
23068         return this;
23069     },
23070
23071     /**
23072      * Called after the component is resized, this method is empty by default but can be implemented by any
23073      * subclass that needs to perform custom logic after a resize occurs.
23074      * @param {Number} adjWidth The box-adjusted width that was set
23075      * @param {Number} adjHeight The box-adjusted height that was set
23076      * @param {Number} rawWidth The width that was originally specified
23077      * @param {Number} rawHeight The height that was originally specified
23078      */
23079     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23080
23081     },
23082
23083     /**
23084      * Called after the component is moved, this method is empty by default but can be implemented by any
23085      * subclass that needs to perform custom logic after a move occurs.
23086      * @param {Number} x The new x position
23087      * @param {Number} y The new y position
23088      */
23089     onPosition : function(x, y){
23090
23091     },
23092
23093     // private
23094     adjustSize : function(w, h){
23095         if(this.autoWidth){
23096             w = 'auto';
23097         }
23098         if(this.autoHeight){
23099             h = 'auto';
23100         }
23101         return {width : w, height: h};
23102     },
23103
23104     // private
23105     adjustPosition : function(x, y){
23106         return {x : x, y: y};
23107     }
23108 });/*
23109  * Based on:
23110  * Ext JS Library 1.1.1
23111  * Copyright(c) 2006-2007, Ext JS, LLC.
23112  *
23113  * Originally Released Under LGPL - original licence link has changed is not relivant.
23114  *
23115  * Fork - LGPL
23116  * <script type="text/javascript">
23117  */
23118
23119
23120 /**
23121  * @class Roo.SplitBar
23122  * @extends Roo.util.Observable
23123  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23124  * <br><br>
23125  * Usage:
23126  * <pre><code>
23127 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23128                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23129 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23130 split.minSize = 100;
23131 split.maxSize = 600;
23132 split.animate = true;
23133 split.on('moved', splitterMoved);
23134 </code></pre>
23135  * @constructor
23136  * Create a new SplitBar
23137  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23138  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23139  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23140  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23141                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23142                         position of the SplitBar).
23143  */
23144 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23145     
23146     /** @private */
23147     this.el = Roo.get(dragElement, true);
23148     this.el.dom.unselectable = "on";
23149     /** @private */
23150     this.resizingEl = Roo.get(resizingElement, true);
23151
23152     /**
23153      * @private
23154      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23155      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23156      * @type Number
23157      */
23158     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23159     
23160     /**
23161      * The minimum size of the resizing element. (Defaults to 0)
23162      * @type Number
23163      */
23164     this.minSize = 0;
23165     
23166     /**
23167      * The maximum size of the resizing element. (Defaults to 2000)
23168      * @type Number
23169      */
23170     this.maxSize = 2000;
23171     
23172     /**
23173      * Whether to animate the transition to the new size
23174      * @type Boolean
23175      */
23176     this.animate = false;
23177     
23178     /**
23179      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23180      * @type Boolean
23181      */
23182     this.useShim = false;
23183     
23184     /** @private */
23185     this.shim = null;
23186     
23187     if(!existingProxy){
23188         /** @private */
23189         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23190     }else{
23191         this.proxy = Roo.get(existingProxy).dom;
23192     }
23193     /** @private */
23194     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23195     
23196     /** @private */
23197     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23198     
23199     /** @private */
23200     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23201     
23202     /** @private */
23203     this.dragSpecs = {};
23204     
23205     /**
23206      * @private The adapter to use to positon and resize elements
23207      */
23208     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23209     this.adapter.init(this);
23210     
23211     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23212         /** @private */
23213         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23214         this.el.addClass("x-splitbar-h");
23215     }else{
23216         /** @private */
23217         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23218         this.el.addClass("x-splitbar-v");
23219     }
23220     
23221     this.addEvents({
23222         /**
23223          * @event resize
23224          * Fires when the splitter is moved (alias for {@link #event-moved})
23225          * @param {Roo.SplitBar} this
23226          * @param {Number} newSize the new width or height
23227          */
23228         "resize" : true,
23229         /**
23230          * @event moved
23231          * Fires when the splitter is moved
23232          * @param {Roo.SplitBar} this
23233          * @param {Number} newSize the new width or height
23234          */
23235         "moved" : true,
23236         /**
23237          * @event beforeresize
23238          * Fires before the splitter is dragged
23239          * @param {Roo.SplitBar} this
23240          */
23241         "beforeresize" : true,
23242
23243         "beforeapply" : true
23244     });
23245
23246     Roo.util.Observable.call(this);
23247 };
23248
23249 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23250     onStartProxyDrag : function(x, y){
23251         this.fireEvent("beforeresize", this);
23252         if(!this.overlay){
23253             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23254             o.unselectable();
23255             o.enableDisplayMode("block");
23256             // all splitbars share the same overlay
23257             Roo.SplitBar.prototype.overlay = o;
23258         }
23259         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23260         this.overlay.show();
23261         Roo.get(this.proxy).setDisplayed("block");
23262         var size = this.adapter.getElementSize(this);
23263         this.activeMinSize = this.getMinimumSize();;
23264         this.activeMaxSize = this.getMaximumSize();;
23265         var c1 = size - this.activeMinSize;
23266         var c2 = Math.max(this.activeMaxSize - size, 0);
23267         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23268             this.dd.resetConstraints();
23269             this.dd.setXConstraint(
23270                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23271                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23272             );
23273             this.dd.setYConstraint(0, 0);
23274         }else{
23275             this.dd.resetConstraints();
23276             this.dd.setXConstraint(0, 0);
23277             this.dd.setYConstraint(
23278                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23279                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23280             );
23281          }
23282         this.dragSpecs.startSize = size;
23283         this.dragSpecs.startPoint = [x, y];
23284         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23285     },
23286     
23287     /** 
23288      * @private Called after the drag operation by the DDProxy
23289      */
23290     onEndProxyDrag : function(e){
23291         Roo.get(this.proxy).setDisplayed(false);
23292         var endPoint = Roo.lib.Event.getXY(e);
23293         if(this.overlay){
23294             this.overlay.hide();
23295         }
23296         var newSize;
23297         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23298             newSize = this.dragSpecs.startSize + 
23299                 (this.placement == Roo.SplitBar.LEFT ?
23300                     endPoint[0] - this.dragSpecs.startPoint[0] :
23301                     this.dragSpecs.startPoint[0] - endPoint[0]
23302                 );
23303         }else{
23304             newSize = this.dragSpecs.startSize + 
23305                 (this.placement == Roo.SplitBar.TOP ?
23306                     endPoint[1] - this.dragSpecs.startPoint[1] :
23307                     this.dragSpecs.startPoint[1] - endPoint[1]
23308                 );
23309         }
23310         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23311         if(newSize != this.dragSpecs.startSize){
23312             if(this.fireEvent('beforeapply', this, newSize) !== false){
23313                 this.adapter.setElementSize(this, newSize);
23314                 this.fireEvent("moved", this, newSize);
23315                 this.fireEvent("resize", this, newSize);
23316             }
23317         }
23318     },
23319     
23320     /**
23321      * Get the adapter this SplitBar uses
23322      * @return The adapter object
23323      */
23324     getAdapter : function(){
23325         return this.adapter;
23326     },
23327     
23328     /**
23329      * Set the adapter this SplitBar uses
23330      * @param {Object} adapter A SplitBar adapter object
23331      */
23332     setAdapter : function(adapter){
23333         this.adapter = adapter;
23334         this.adapter.init(this);
23335     },
23336     
23337     /**
23338      * Gets the minimum size for the resizing element
23339      * @return {Number} The minimum size
23340      */
23341     getMinimumSize : function(){
23342         return this.minSize;
23343     },
23344     
23345     /**
23346      * Sets the minimum size for the resizing element
23347      * @param {Number} minSize The minimum size
23348      */
23349     setMinimumSize : function(minSize){
23350         this.minSize = minSize;
23351     },
23352     
23353     /**
23354      * Gets the maximum size for the resizing element
23355      * @return {Number} The maximum size
23356      */
23357     getMaximumSize : function(){
23358         return this.maxSize;
23359     },
23360     
23361     /**
23362      * Sets the maximum size for the resizing element
23363      * @param {Number} maxSize The maximum size
23364      */
23365     setMaximumSize : function(maxSize){
23366         this.maxSize = maxSize;
23367     },
23368     
23369     /**
23370      * Sets the initialize size for the resizing element
23371      * @param {Number} size The initial size
23372      */
23373     setCurrentSize : function(size){
23374         var oldAnimate = this.animate;
23375         this.animate = false;
23376         this.adapter.setElementSize(this, size);
23377         this.animate = oldAnimate;
23378     },
23379     
23380     /**
23381      * Destroy this splitbar. 
23382      * @param {Boolean} removeEl True to remove the element
23383      */
23384     destroy : function(removeEl){
23385         if(this.shim){
23386             this.shim.remove();
23387         }
23388         this.dd.unreg();
23389         this.proxy.parentNode.removeChild(this.proxy);
23390         if(removeEl){
23391             this.el.remove();
23392         }
23393     }
23394 });
23395
23396 /**
23397  * @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.
23398  */
23399 Roo.SplitBar.createProxy = function(dir){
23400     var proxy = new Roo.Element(document.createElement("div"));
23401     proxy.unselectable();
23402     var cls = 'x-splitbar-proxy';
23403     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23404     document.body.appendChild(proxy.dom);
23405     return proxy.dom;
23406 };
23407
23408 /** 
23409  * @class Roo.SplitBar.BasicLayoutAdapter
23410  * Default Adapter. It assumes the splitter and resizing element are not positioned
23411  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23412  */
23413 Roo.SplitBar.BasicLayoutAdapter = function(){
23414 };
23415
23416 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23417     // do nothing for now
23418     init : function(s){
23419     
23420     },
23421     /**
23422      * Called before drag operations to get the current size of the resizing element. 
23423      * @param {Roo.SplitBar} s The SplitBar using this adapter
23424      */
23425      getElementSize : function(s){
23426         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23427             return s.resizingEl.getWidth();
23428         }else{
23429             return s.resizingEl.getHeight();
23430         }
23431     },
23432     
23433     /**
23434      * Called after drag operations to set the size of the resizing element.
23435      * @param {Roo.SplitBar} s The SplitBar using this adapter
23436      * @param {Number} newSize The new size to set
23437      * @param {Function} onComplete A function to be invoked when resizing is complete
23438      */
23439     setElementSize : function(s, newSize, onComplete){
23440         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23441             if(!s.animate){
23442                 s.resizingEl.setWidth(newSize);
23443                 if(onComplete){
23444                     onComplete(s, newSize);
23445                 }
23446             }else{
23447                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23448             }
23449         }else{
23450             
23451             if(!s.animate){
23452                 s.resizingEl.setHeight(newSize);
23453                 if(onComplete){
23454                     onComplete(s, newSize);
23455                 }
23456             }else{
23457                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23458             }
23459         }
23460     }
23461 };
23462
23463 /** 
23464  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23465  * @extends Roo.SplitBar.BasicLayoutAdapter
23466  * Adapter that  moves the splitter element to align with the resized sizing element. 
23467  * Used with an absolute positioned SplitBar.
23468  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23469  * document.body, make sure you assign an id to the body element.
23470  */
23471 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23472     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23473     this.container = Roo.get(container);
23474 };
23475
23476 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23477     init : function(s){
23478         this.basic.init(s);
23479     },
23480     
23481     getElementSize : function(s){
23482         return this.basic.getElementSize(s);
23483     },
23484     
23485     setElementSize : function(s, newSize, onComplete){
23486         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23487     },
23488     
23489     moveSplitter : function(s){
23490         var yes = Roo.SplitBar;
23491         switch(s.placement){
23492             case yes.LEFT:
23493                 s.el.setX(s.resizingEl.getRight());
23494                 break;
23495             case yes.RIGHT:
23496                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23497                 break;
23498             case yes.TOP:
23499                 s.el.setY(s.resizingEl.getBottom());
23500                 break;
23501             case yes.BOTTOM:
23502                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23503                 break;
23504         }
23505     }
23506 };
23507
23508 /**
23509  * Orientation constant - Create a vertical SplitBar
23510  * @static
23511  * @type Number
23512  */
23513 Roo.SplitBar.VERTICAL = 1;
23514
23515 /**
23516  * Orientation constant - Create a horizontal SplitBar
23517  * @static
23518  * @type Number
23519  */
23520 Roo.SplitBar.HORIZONTAL = 2;
23521
23522 /**
23523  * Placement constant - The resizing element is to the left of the splitter element
23524  * @static
23525  * @type Number
23526  */
23527 Roo.SplitBar.LEFT = 1;
23528
23529 /**
23530  * Placement constant - The resizing element is to the right of the splitter element
23531  * @static
23532  * @type Number
23533  */
23534 Roo.SplitBar.RIGHT = 2;
23535
23536 /**
23537  * Placement constant - The resizing element is positioned above the splitter element
23538  * @static
23539  * @type Number
23540  */
23541 Roo.SplitBar.TOP = 3;
23542
23543 /**
23544  * Placement constant - The resizing element is positioned under splitter element
23545  * @static
23546  * @type Number
23547  */
23548 Roo.SplitBar.BOTTOM = 4;
23549 /*
23550  * Based on:
23551  * Ext JS Library 1.1.1
23552  * Copyright(c) 2006-2007, Ext JS, LLC.
23553  *
23554  * Originally Released Under LGPL - original licence link has changed is not relivant.
23555  *
23556  * Fork - LGPL
23557  * <script type="text/javascript">
23558  */
23559
23560 /**
23561  * @class Roo.View
23562  * @extends Roo.util.Observable
23563  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23564  * This class also supports single and multi selection modes. <br>
23565  * Create a data model bound view:
23566  <pre><code>
23567  var store = new Roo.data.Store(...);
23568
23569  var view = new Roo.View({
23570     el : "my-element",
23571     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23572  
23573     singleSelect: true,
23574     selectedClass: "ydataview-selected",
23575     store: store
23576  });
23577
23578  // listen for node click?
23579  view.on("click", function(vw, index, node, e){
23580  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23581  });
23582
23583  // load XML data
23584  dataModel.load("foobar.xml");
23585  </code></pre>
23586  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23587  * <br><br>
23588  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23589  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23590  * 
23591  * Note: old style constructor is still suported (container, template, config)
23592  * 
23593  * @constructor
23594  * Create a new View
23595  * @param {Object} config The config object
23596  * 
23597  */
23598 Roo.View = function(config, depreciated_tpl, depreciated_config){
23599     
23600     if (typeof(depreciated_tpl) == 'undefined') {
23601         // new way.. - universal constructor.
23602         Roo.apply(this, config);
23603         this.el  = Roo.get(this.el);
23604     } else {
23605         // old format..
23606         this.el  = Roo.get(config);
23607         this.tpl = depreciated_tpl;
23608         Roo.apply(this, depreciated_config);
23609     }
23610      
23611     
23612     if(typeof(this.tpl) == "string"){
23613         this.tpl = new Roo.Template(this.tpl);
23614     } else {
23615         // support xtype ctors..
23616         this.tpl = new Roo.factory(this.tpl, Roo);
23617     }
23618     
23619     
23620     this.tpl.compile();
23621    
23622
23623      
23624     /** @private */
23625     this.addEvents({
23626         /**
23627          * @event beforeclick
23628          * Fires before a click is processed. Returns false to cancel the default action.
23629          * @param {Roo.View} this
23630          * @param {Number} index The index of the target node
23631          * @param {HTMLElement} node The target node
23632          * @param {Roo.EventObject} e The raw event object
23633          */
23634             "beforeclick" : true,
23635         /**
23636          * @event click
23637          * Fires when a template node is clicked.
23638          * @param {Roo.View} this
23639          * @param {Number} index The index of the target node
23640          * @param {HTMLElement} node The target node
23641          * @param {Roo.EventObject} e The raw event object
23642          */
23643             "click" : true,
23644         /**
23645          * @event dblclick
23646          * Fires when a template node is double clicked.
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             "dblclick" : true,
23653         /**
23654          * @event contextmenu
23655          * Fires when a template node is right 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             "contextmenu" : true,
23662         /**
23663          * @event selectionchange
23664          * Fires when the selected nodes change.
23665          * @param {Roo.View} this
23666          * @param {Array} selections Array of the selected nodes
23667          */
23668             "selectionchange" : true,
23669     
23670         /**
23671          * @event beforeselect
23672          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23673          * @param {Roo.View} this
23674          * @param {HTMLElement} node The node to be selected
23675          * @param {Array} selections Array of currently selected nodes
23676          */
23677             "beforeselect" : true,
23678         /**
23679          * @event preparedata
23680          * Fires on every row to render, to allow you to change the data.
23681          * @param {Roo.View} this
23682          * @param {Object} data to be rendered (change this)
23683          */
23684           "preparedata" : true
23685         });
23686
23687     this.el.on({
23688         "click": this.onClick,
23689         "dblclick": this.onDblClick,
23690         "contextmenu": this.onContextMenu,
23691         scope:this
23692     });
23693
23694     this.selections = [];
23695     this.nodes = [];
23696     this.cmp = new Roo.CompositeElementLite([]);
23697     if(this.store){
23698         this.store = Roo.factory(this.store, Roo.data);
23699         this.setStore(this.store, true);
23700     }
23701     Roo.View.superclass.constructor.call(this);
23702 };
23703
23704 Roo.extend(Roo.View, Roo.util.Observable, {
23705     
23706      /**
23707      * @cfg {Roo.data.Store} store Data store to load data from.
23708      */
23709     store : false,
23710     
23711     /**
23712      * @cfg {String|Roo.Element} el The container element.
23713      */
23714     el : '',
23715     
23716     /**
23717      * @cfg {String|Roo.Template} tpl The template used by this View 
23718      */
23719     tpl : false,
23720     
23721     /**
23722      * @cfg {String} selectedClass The css class to add to selected nodes
23723      */
23724     selectedClass : "x-view-selected",
23725      /**
23726      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23727      */
23728     emptyText : "",
23729     /**
23730      * @cfg {Boolean} multiSelect Allow multiple selection
23731      */
23732     multiSelect : false,
23733     /**
23734      * @cfg {Boolean} singleSelect Allow single selection
23735      */
23736     singleSelect:  false,
23737     
23738     /**
23739      * @cfg {Boolean} toggleSelect - selecting 
23740      */
23741     toggleSelect : false,
23742     
23743     /**
23744      * Returns the element this view is bound to.
23745      * @return {Roo.Element}
23746      */
23747     getEl : function(){
23748         return this.el;
23749     },
23750
23751     /**
23752      * Refreshes the view.
23753      */
23754     refresh : function(){
23755         var t = this.tpl;
23756         this.clearSelections();
23757         this.el.update("");
23758         var html = [];
23759         var records = this.store.getRange();
23760         if(records.length < 1){
23761             this.el.update(this.emptyText);
23762             return;
23763         }
23764         for(var i = 0, len = records.length; i < len; i++){
23765             var data = this.prepareData(records[i].data, i, records[i]);
23766             this.fireEvent("preparedata", this, data, i, records[i]);
23767             html[html.length] = t.apply(data);
23768         }
23769         this.el.update(html.join(""));
23770         this.nodes = this.el.dom.childNodes;
23771         this.updateIndexes(0);
23772     },
23773
23774     /**
23775      * Function to override to reformat the data that is sent to
23776      * the template for each node.
23777      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23778      * a JSON object for an UpdateManager bound view).
23779      */
23780     prepareData : function(data){
23781         return data;
23782     },
23783
23784     onUpdate : function(ds, record){
23785         this.clearSelections();
23786         var index = this.store.indexOf(record);
23787         var n = this.nodes[index];
23788         this.tpl.insertBefore(n, this.prepareData(record.data));
23789         n.parentNode.removeChild(n);
23790         this.updateIndexes(index, index);
23791     },
23792
23793     onAdd : function(ds, records, index){
23794         this.clearSelections();
23795         if(this.nodes.length == 0){
23796             this.refresh();
23797             return;
23798         }
23799         var n = this.nodes[index];
23800         for(var i = 0, len = records.length; i < len; i++){
23801             var d = this.prepareData(records[i].data);
23802             if(n){
23803                 this.tpl.insertBefore(n, d);
23804             }else{
23805                 this.tpl.append(this.el, d);
23806             }
23807         }
23808         this.updateIndexes(index);
23809     },
23810
23811     onRemove : function(ds, record, index){
23812         this.clearSelections();
23813         this.el.dom.removeChild(this.nodes[index]);
23814         this.updateIndexes(index);
23815     },
23816
23817     /**
23818      * Refresh an individual node.
23819      * @param {Number} index
23820      */
23821     refreshNode : function(index){
23822         this.onUpdate(this.store, this.store.getAt(index));
23823     },
23824
23825     updateIndexes : function(startIndex, endIndex){
23826         var ns = this.nodes;
23827         startIndex = startIndex || 0;
23828         endIndex = endIndex || ns.length - 1;
23829         for(var i = startIndex; i <= endIndex; i++){
23830             ns[i].nodeIndex = i;
23831         }
23832     },
23833
23834     /**
23835      * Changes the data store this view uses and refresh the view.
23836      * @param {Store} store
23837      */
23838     setStore : function(store, initial){
23839         if(!initial && this.store){
23840             this.store.un("datachanged", this.refresh);
23841             this.store.un("add", this.onAdd);
23842             this.store.un("remove", this.onRemove);
23843             this.store.un("update", this.onUpdate);
23844             this.store.un("clear", this.refresh);
23845         }
23846         if(store){
23847           
23848             store.on("datachanged", this.refresh, this);
23849             store.on("add", this.onAdd, this);
23850             store.on("remove", this.onRemove, this);
23851             store.on("update", this.onUpdate, this);
23852             store.on("clear", this.refresh, this);
23853         }
23854         
23855         if(store){
23856             this.refresh();
23857         }
23858     },
23859
23860     /**
23861      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23862      * @param {HTMLElement} node
23863      * @return {HTMLElement} The template node
23864      */
23865     findItemFromChild : function(node){
23866         var el = this.el.dom;
23867         if(!node || node.parentNode == el){
23868                     return node;
23869             }
23870             var p = node.parentNode;
23871             while(p && p != el){
23872             if(p.parentNode == el){
23873                 return p;
23874             }
23875             p = p.parentNode;
23876         }
23877             return null;
23878     },
23879
23880     /** @ignore */
23881     onClick : function(e){
23882         var item = this.findItemFromChild(e.getTarget());
23883         if(item){
23884             var index = this.indexOf(item);
23885             if(this.onItemClick(item, index, e) !== false){
23886                 this.fireEvent("click", this, index, item, e);
23887             }
23888         }else{
23889             this.clearSelections();
23890         }
23891     },
23892
23893     /** @ignore */
23894     onContextMenu : function(e){
23895         var item = this.findItemFromChild(e.getTarget());
23896         if(item){
23897             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23898         }
23899     },
23900
23901     /** @ignore */
23902     onDblClick : function(e){
23903         var item = this.findItemFromChild(e.getTarget());
23904         if(item){
23905             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23906         }
23907     },
23908
23909     onItemClick : function(item, index, e)
23910     {
23911         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23912             return false;
23913         }
23914         if (this.toggleSelect) {
23915             var m = this.isSelected(item) ? 'unselect' : 'select';
23916             Roo.log(m);
23917             var _t = this;
23918             _t[m](item, true, false);
23919             return true;
23920         }
23921         if(this.multiSelect || this.singleSelect){
23922             if(this.multiSelect && e.shiftKey && this.lastSelection){
23923                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23924             }else{
23925                 this.select(item, this.multiSelect && e.ctrlKey);
23926                 this.lastSelection = item;
23927             }
23928             e.preventDefault();
23929         }
23930         return true;
23931     },
23932
23933     /**
23934      * Get the number of selected nodes.
23935      * @return {Number}
23936      */
23937     getSelectionCount : function(){
23938         return this.selections.length;
23939     },
23940
23941     /**
23942      * Get the currently selected nodes.
23943      * @return {Array} An array of HTMLElements
23944      */
23945     getSelectedNodes : function(){
23946         return this.selections;
23947     },
23948
23949     /**
23950      * Get the indexes of the selected nodes.
23951      * @return {Array}
23952      */
23953     getSelectedIndexes : function(){
23954         var indexes = [], s = this.selections;
23955         for(var i = 0, len = s.length; i < len; i++){
23956             indexes.push(s[i].nodeIndex);
23957         }
23958         return indexes;
23959     },
23960
23961     /**
23962      * Clear all selections
23963      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23964      */
23965     clearSelections : function(suppressEvent){
23966         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23967             this.cmp.elements = this.selections;
23968             this.cmp.removeClass(this.selectedClass);
23969             this.selections = [];
23970             if(!suppressEvent){
23971                 this.fireEvent("selectionchange", this, this.selections);
23972             }
23973         }
23974     },
23975
23976     /**
23977      * Returns true if the passed node is selected
23978      * @param {HTMLElement/Number} node The node or node index
23979      * @return {Boolean}
23980      */
23981     isSelected : function(node){
23982         var s = this.selections;
23983         if(s.length < 1){
23984             return false;
23985         }
23986         node = this.getNode(node);
23987         return s.indexOf(node) !== -1;
23988     },
23989
23990     /**
23991      * Selects nodes.
23992      * @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
23993      * @param {Boolean} keepExisting (optional) true to keep existing selections
23994      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23995      */
23996     select : function(nodeInfo, keepExisting, suppressEvent){
23997         if(nodeInfo instanceof Array){
23998             if(!keepExisting){
23999                 this.clearSelections(true);
24000             }
24001             for(var i = 0, len = nodeInfo.length; i < len; i++){
24002                 this.select(nodeInfo[i], true, true);
24003             }
24004             return;
24005         } 
24006         var node = this.getNode(nodeInfo);
24007         if(!node || this.isSelected(node)){
24008             return; // already selected.
24009         }
24010         if(!keepExisting){
24011             this.clearSelections(true);
24012         }
24013         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24014             Roo.fly(node).addClass(this.selectedClass);
24015             this.selections.push(node);
24016             if(!suppressEvent){
24017                 this.fireEvent("selectionchange", this, this.selections);
24018             }
24019         }
24020         
24021         
24022     },
24023       /**
24024      * Unselects nodes.
24025      * @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
24026      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24027      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24028      */
24029     unselect : function(nodeInfo, keepExisting, suppressEvent)
24030     {
24031         if(nodeInfo instanceof Array){
24032             Roo.each(this.selections, function(s) {
24033                 this.unselect(s, nodeInfo);
24034             }, this);
24035             return;
24036         }
24037         var node = this.getNode(nodeInfo);
24038         if(!node || !this.isSelected(node)){
24039             Roo.log("not selected");
24040             return; // not selected.
24041         }
24042         // fireevent???
24043         var ns = [];
24044         Roo.each(this.selections, function(s) {
24045             if (s == node ) {
24046                 Roo.fly(node).removeClass(this.selectedClass);
24047
24048                 return;
24049             }
24050             ns.push(s);
24051         },this);
24052         
24053         this.selections= ns;
24054         this.fireEvent("selectionchange", this, this.selections);
24055     },
24056
24057     /**
24058      * Gets a template node.
24059      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24060      * @return {HTMLElement} The node or null if it wasn't found
24061      */
24062     getNode : function(nodeInfo){
24063         if(typeof nodeInfo == "string"){
24064             return document.getElementById(nodeInfo);
24065         }else if(typeof nodeInfo == "number"){
24066             return this.nodes[nodeInfo];
24067         }
24068         return nodeInfo;
24069     },
24070
24071     /**
24072      * Gets a range template nodes.
24073      * @param {Number} startIndex
24074      * @param {Number} endIndex
24075      * @return {Array} An array of nodes
24076      */
24077     getNodes : function(start, end){
24078         var ns = this.nodes;
24079         start = start || 0;
24080         end = typeof end == "undefined" ? ns.length - 1 : end;
24081         var nodes = [];
24082         if(start <= end){
24083             for(var i = start; i <= end; i++){
24084                 nodes.push(ns[i]);
24085             }
24086         } else{
24087             for(var i = start; i >= end; i--){
24088                 nodes.push(ns[i]);
24089             }
24090         }
24091         return nodes;
24092     },
24093
24094     /**
24095      * Finds the index of the passed node
24096      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24097      * @return {Number} The index of the node or -1
24098      */
24099     indexOf : function(node){
24100         node = this.getNode(node);
24101         if(typeof node.nodeIndex == "number"){
24102             return node.nodeIndex;
24103         }
24104         var ns = this.nodes;
24105         for(var i = 0, len = ns.length; i < len; i++){
24106             if(ns[i] == node){
24107                 return i;
24108             }
24109         }
24110         return -1;
24111     }
24112 });
24113 /*
24114  * Based on:
24115  * Ext JS Library 1.1.1
24116  * Copyright(c) 2006-2007, Ext JS, LLC.
24117  *
24118  * Originally Released Under LGPL - original licence link has changed is not relivant.
24119  *
24120  * Fork - LGPL
24121  * <script type="text/javascript">
24122  */
24123
24124 /**
24125  * @class Roo.JsonView
24126  * @extends Roo.View
24127  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24128 <pre><code>
24129 var view = new Roo.JsonView({
24130     container: "my-element",
24131     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24132     multiSelect: true, 
24133     jsonRoot: "data" 
24134 });
24135
24136 // listen for node click?
24137 view.on("click", function(vw, index, node, e){
24138     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24139 });
24140
24141 // direct load of JSON data
24142 view.load("foobar.php");
24143
24144 // Example from my blog list
24145 var tpl = new Roo.Template(
24146     '&lt;div class="entry"&gt;' +
24147     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24148     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24149     "&lt;/div&gt;&lt;hr /&gt;"
24150 );
24151
24152 var moreView = new Roo.JsonView({
24153     container :  "entry-list", 
24154     template : tpl,
24155     jsonRoot: "posts"
24156 });
24157 moreView.on("beforerender", this.sortEntries, this);
24158 moreView.load({
24159     url: "/blog/get-posts.php",
24160     params: "allposts=true",
24161     text: "Loading Blog Entries..."
24162 });
24163 </code></pre>
24164
24165 * Note: old code is supported with arguments : (container, template, config)
24166
24167
24168  * @constructor
24169  * Create a new JsonView
24170  * 
24171  * @param {Object} config The config object
24172  * 
24173  */
24174 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24175     
24176     
24177     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24178
24179     var um = this.el.getUpdateManager();
24180     um.setRenderer(this);
24181     um.on("update", this.onLoad, this);
24182     um.on("failure", this.onLoadException, this);
24183
24184     /**
24185      * @event beforerender
24186      * Fires before rendering of the downloaded JSON data.
24187      * @param {Roo.JsonView} this
24188      * @param {Object} data The JSON data loaded
24189      */
24190     /**
24191      * @event load
24192      * Fires when data is loaded.
24193      * @param {Roo.JsonView} this
24194      * @param {Object} data The JSON data loaded
24195      * @param {Object} response The raw Connect response object
24196      */
24197     /**
24198      * @event loadexception
24199      * Fires when loading fails.
24200      * @param {Roo.JsonView} this
24201      * @param {Object} response The raw Connect response object
24202      */
24203     this.addEvents({
24204         'beforerender' : true,
24205         'load' : true,
24206         'loadexception' : true
24207     });
24208 };
24209 Roo.extend(Roo.JsonView, Roo.View, {
24210     /**
24211      * @type {String} The root property in the loaded JSON object that contains the data
24212      */
24213     jsonRoot : "",
24214
24215     /**
24216      * Refreshes the view.
24217      */
24218     refresh : function(){
24219         this.clearSelections();
24220         this.el.update("");
24221         var html = [];
24222         var o = this.jsonData;
24223         if(o && o.length > 0){
24224             for(var i = 0, len = o.length; i < len; i++){
24225                 var data = this.prepareData(o[i], i, o);
24226                 html[html.length] = this.tpl.apply(data);
24227             }
24228         }else{
24229             html.push(this.emptyText);
24230         }
24231         this.el.update(html.join(""));
24232         this.nodes = this.el.dom.childNodes;
24233         this.updateIndexes(0);
24234     },
24235
24236     /**
24237      * 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.
24238      * @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:
24239      <pre><code>
24240      view.load({
24241          url: "your-url.php",
24242          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24243          callback: yourFunction,
24244          scope: yourObject, //(optional scope)
24245          discardUrl: false,
24246          nocache: false,
24247          text: "Loading...",
24248          timeout: 30,
24249          scripts: false
24250      });
24251      </code></pre>
24252      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24253      * 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.
24254      * @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}
24255      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24256      * @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.
24257      */
24258     load : function(){
24259         var um = this.el.getUpdateManager();
24260         um.update.apply(um, arguments);
24261     },
24262
24263     render : function(el, response){
24264         this.clearSelections();
24265         this.el.update("");
24266         var o;
24267         try{
24268             o = Roo.util.JSON.decode(response.responseText);
24269             if(this.jsonRoot){
24270                 
24271                 o = o[this.jsonRoot];
24272             }
24273         } catch(e){
24274         }
24275         /**
24276          * The current JSON data or null
24277          */
24278         this.jsonData = o;
24279         this.beforeRender();
24280         this.refresh();
24281     },
24282
24283 /**
24284  * Get the number of records in the current JSON dataset
24285  * @return {Number}
24286  */
24287     getCount : function(){
24288         return this.jsonData ? this.jsonData.length : 0;
24289     },
24290
24291 /**
24292  * Returns the JSON object for the specified node(s)
24293  * @param {HTMLElement/Array} node The node or an array of nodes
24294  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24295  * you get the JSON object for the node
24296  */
24297     getNodeData : function(node){
24298         if(node instanceof Array){
24299             var data = [];
24300             for(var i = 0, len = node.length; i < len; i++){
24301                 data.push(this.getNodeData(node[i]));
24302             }
24303             return data;
24304         }
24305         return this.jsonData[this.indexOf(node)] || null;
24306     },
24307
24308     beforeRender : function(){
24309         this.snapshot = this.jsonData;
24310         if(this.sortInfo){
24311             this.sort.apply(this, this.sortInfo);
24312         }
24313         this.fireEvent("beforerender", this, this.jsonData);
24314     },
24315
24316     onLoad : function(el, o){
24317         this.fireEvent("load", this, this.jsonData, o);
24318     },
24319
24320     onLoadException : function(el, o){
24321         this.fireEvent("loadexception", this, o);
24322     },
24323
24324 /**
24325  * Filter the data by a specific property.
24326  * @param {String} property A property on your JSON objects
24327  * @param {String/RegExp} value Either string that the property values
24328  * should start with, or a RegExp to test against the property
24329  */
24330     filter : function(property, value){
24331         if(this.jsonData){
24332             var data = [];
24333             var ss = this.snapshot;
24334             if(typeof value == "string"){
24335                 var vlen = value.length;
24336                 if(vlen == 0){
24337                     this.clearFilter();
24338                     return;
24339                 }
24340                 value = value.toLowerCase();
24341                 for(var i = 0, len = ss.length; i < len; i++){
24342                     var o = ss[i];
24343                     if(o[property].substr(0, vlen).toLowerCase() == value){
24344                         data.push(o);
24345                     }
24346                 }
24347             } else if(value.exec){ // regex?
24348                 for(var i = 0, len = ss.length; i < len; i++){
24349                     var o = ss[i];
24350                     if(value.test(o[property])){
24351                         data.push(o);
24352                     }
24353                 }
24354             } else{
24355                 return;
24356             }
24357             this.jsonData = data;
24358             this.refresh();
24359         }
24360     },
24361
24362 /**
24363  * Filter by a function. The passed function will be called with each
24364  * object in the current dataset. If the function returns true the value is kept,
24365  * otherwise it is filtered.
24366  * @param {Function} fn
24367  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24368  */
24369     filterBy : function(fn, scope){
24370         if(this.jsonData){
24371             var data = [];
24372             var ss = this.snapshot;
24373             for(var i = 0, len = ss.length; i < len; i++){
24374                 var o = ss[i];
24375                 if(fn.call(scope || this, o)){
24376                     data.push(o);
24377                 }
24378             }
24379             this.jsonData = data;
24380             this.refresh();
24381         }
24382     },
24383
24384 /**
24385  * Clears the current filter.
24386  */
24387     clearFilter : function(){
24388         if(this.snapshot && this.jsonData != this.snapshot){
24389             this.jsonData = this.snapshot;
24390             this.refresh();
24391         }
24392     },
24393
24394
24395 /**
24396  * Sorts the data for this view and refreshes it.
24397  * @param {String} property A property on your JSON objects to sort on
24398  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24399  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24400  */
24401     sort : function(property, dir, sortType){
24402         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24403         if(this.jsonData){
24404             var p = property;
24405             var dsc = dir && dir.toLowerCase() == "desc";
24406             var f = function(o1, o2){
24407                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24408                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24409                 ;
24410                 if(v1 < v2){
24411                     return dsc ? +1 : -1;
24412                 } else if(v1 > v2){
24413                     return dsc ? -1 : +1;
24414                 } else{
24415                     return 0;
24416                 }
24417             };
24418             this.jsonData.sort(f);
24419             this.refresh();
24420             if(this.jsonData != this.snapshot){
24421                 this.snapshot.sort(f);
24422             }
24423         }
24424     }
24425 });/*
24426  * Based on:
24427  * Ext JS Library 1.1.1
24428  * Copyright(c) 2006-2007, Ext JS, LLC.
24429  *
24430  * Originally Released Under LGPL - original licence link has changed is not relivant.
24431  *
24432  * Fork - LGPL
24433  * <script type="text/javascript">
24434  */
24435  
24436
24437 /**
24438  * @class Roo.ColorPalette
24439  * @extends Roo.Component
24440  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24441  * Here's an example of typical usage:
24442  * <pre><code>
24443 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24444 cp.render('my-div');
24445
24446 cp.on('select', function(palette, selColor){
24447     // do something with selColor
24448 });
24449 </code></pre>
24450  * @constructor
24451  * Create a new ColorPalette
24452  * @param {Object} config The config object
24453  */
24454 Roo.ColorPalette = function(config){
24455     Roo.ColorPalette.superclass.constructor.call(this, config);
24456     this.addEvents({
24457         /**
24458              * @event select
24459              * Fires when a color is selected
24460              * @param {ColorPalette} this
24461              * @param {String} color The 6-digit color hex code (without the # symbol)
24462              */
24463         select: true
24464     });
24465
24466     if(this.handler){
24467         this.on("select", this.handler, this.scope, true);
24468     }
24469 };
24470 Roo.extend(Roo.ColorPalette, Roo.Component, {
24471     /**
24472      * @cfg {String} itemCls
24473      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24474      */
24475     itemCls : "x-color-palette",
24476     /**
24477      * @cfg {String} value
24478      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24479      * the hex codes are case-sensitive.
24480      */
24481     value : null,
24482     clickEvent:'click',
24483     // private
24484     ctype: "Roo.ColorPalette",
24485
24486     /**
24487      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24488      */
24489     allowReselect : false,
24490
24491     /**
24492      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24493      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24494      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24495      * of colors with the width setting until the box is symmetrical.</p>
24496      * <p>You can override individual colors if needed:</p>
24497      * <pre><code>
24498 var cp = new Roo.ColorPalette();
24499 cp.colors[0] = "FF0000";  // change the first box to red
24500 </code></pre>
24501
24502 Or you can provide a custom array of your own for complete control:
24503 <pre><code>
24504 var cp = new Roo.ColorPalette();
24505 cp.colors = ["000000", "993300", "333300"];
24506 </code></pre>
24507      * @type Array
24508      */
24509     colors : [
24510         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24511         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24512         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24513         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24514         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24515     ],
24516
24517     // private
24518     onRender : function(container, position){
24519         var t = new Roo.MasterTemplate(
24520             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24521         );
24522         var c = this.colors;
24523         for(var i = 0, len = c.length; i < len; i++){
24524             t.add([c[i]]);
24525         }
24526         var el = document.createElement("div");
24527         el.className = this.itemCls;
24528         t.overwrite(el);
24529         container.dom.insertBefore(el, position);
24530         this.el = Roo.get(el);
24531         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24532         if(this.clickEvent != 'click'){
24533             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24534         }
24535     },
24536
24537     // private
24538     afterRender : function(){
24539         Roo.ColorPalette.superclass.afterRender.call(this);
24540         if(this.value){
24541             var s = this.value;
24542             this.value = null;
24543             this.select(s);
24544         }
24545     },
24546
24547     // private
24548     handleClick : function(e, t){
24549         e.preventDefault();
24550         if(!this.disabled){
24551             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24552             this.select(c.toUpperCase());
24553         }
24554     },
24555
24556     /**
24557      * Selects the specified color in the palette (fires the select event)
24558      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24559      */
24560     select : function(color){
24561         color = color.replace("#", "");
24562         if(color != this.value || this.allowReselect){
24563             var el = this.el;
24564             if(this.value){
24565                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24566             }
24567             el.child("a.color-"+color).addClass("x-color-palette-sel");
24568             this.value = color;
24569             this.fireEvent("select", this, color);
24570         }
24571     }
24572 });/*
24573  * Based on:
24574  * Ext JS Library 1.1.1
24575  * Copyright(c) 2006-2007, Ext JS, LLC.
24576  *
24577  * Originally Released Under LGPL - original licence link has changed is not relivant.
24578  *
24579  * Fork - LGPL
24580  * <script type="text/javascript">
24581  */
24582  
24583 /**
24584  * @class Roo.DatePicker
24585  * @extends Roo.Component
24586  * Simple date picker class.
24587  * @constructor
24588  * Create a new DatePicker
24589  * @param {Object} config The config object
24590  */
24591 Roo.DatePicker = function(config){
24592     Roo.DatePicker.superclass.constructor.call(this, config);
24593
24594     this.value = config && config.value ?
24595                  config.value.clearTime() : new Date().clearTime();
24596
24597     this.addEvents({
24598         /**
24599              * @event select
24600              * Fires when a date is selected
24601              * @param {DatePicker} this
24602              * @param {Date} date The selected date
24603              */
24604         'select': true,
24605         /**
24606              * @event monthchange
24607              * Fires when the displayed month changes 
24608              * @param {DatePicker} this
24609              * @param {Date} date The selected month
24610              */
24611         'monthchange': true
24612     });
24613
24614     if(this.handler){
24615         this.on("select", this.handler,  this.scope || this);
24616     }
24617     // build the disabledDatesRE
24618     if(!this.disabledDatesRE && this.disabledDates){
24619         var dd = this.disabledDates;
24620         var re = "(?:";
24621         for(var i = 0; i < dd.length; i++){
24622             re += dd[i];
24623             if(i != dd.length-1) re += "|";
24624         }
24625         this.disabledDatesRE = new RegExp(re + ")");
24626     }
24627 };
24628
24629 Roo.extend(Roo.DatePicker, Roo.Component, {
24630     /**
24631      * @cfg {String} todayText
24632      * The text to display on the button that selects the current date (defaults to "Today")
24633      */
24634     todayText : "Today",
24635     /**
24636      * @cfg {String} okText
24637      * The text to display on the ok button
24638      */
24639     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24640     /**
24641      * @cfg {String} cancelText
24642      * The text to display on the cancel button
24643      */
24644     cancelText : "Cancel",
24645     /**
24646      * @cfg {String} todayTip
24647      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24648      */
24649     todayTip : "{0} (Spacebar)",
24650     /**
24651      * @cfg {Date} minDate
24652      * Minimum allowable date (JavaScript date object, defaults to null)
24653      */
24654     minDate : null,
24655     /**
24656      * @cfg {Date} maxDate
24657      * Maximum allowable date (JavaScript date object, defaults to null)
24658      */
24659     maxDate : null,
24660     /**
24661      * @cfg {String} minText
24662      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24663      */
24664     minText : "This date is before the minimum date",
24665     /**
24666      * @cfg {String} maxText
24667      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24668      */
24669     maxText : "This date is after the maximum date",
24670     /**
24671      * @cfg {String} format
24672      * The default date format string which can be overriden for localization support.  The format must be
24673      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24674      */
24675     format : "m/d/y",
24676     /**
24677      * @cfg {Array} disabledDays
24678      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24679      */
24680     disabledDays : null,
24681     /**
24682      * @cfg {String} disabledDaysText
24683      * The tooltip to display when the date falls on a disabled day (defaults to "")
24684      */
24685     disabledDaysText : "",
24686     /**
24687      * @cfg {RegExp} disabledDatesRE
24688      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24689      */
24690     disabledDatesRE : null,
24691     /**
24692      * @cfg {String} disabledDatesText
24693      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24694      */
24695     disabledDatesText : "",
24696     /**
24697      * @cfg {Boolean} constrainToViewport
24698      * True to constrain the date picker to the viewport (defaults to true)
24699      */
24700     constrainToViewport : true,
24701     /**
24702      * @cfg {Array} monthNames
24703      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24704      */
24705     monthNames : Date.monthNames,
24706     /**
24707      * @cfg {Array} dayNames
24708      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24709      */
24710     dayNames : Date.dayNames,
24711     /**
24712      * @cfg {String} nextText
24713      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24714      */
24715     nextText: 'Next Month (Control+Right)',
24716     /**
24717      * @cfg {String} prevText
24718      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24719      */
24720     prevText: 'Previous Month (Control+Left)',
24721     /**
24722      * @cfg {String} monthYearText
24723      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24724      */
24725     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24726     /**
24727      * @cfg {Number} startDay
24728      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24729      */
24730     startDay : 0,
24731     /**
24732      * @cfg {Bool} showClear
24733      * Show a clear button (usefull for date form elements that can be blank.)
24734      */
24735     
24736     showClear: false,
24737     
24738     /**
24739      * Sets the value of the date field
24740      * @param {Date} value The date to set
24741      */
24742     setValue : function(value){
24743         var old = this.value;
24744         this.value = value.clearTime(true);
24745         if(this.el){
24746             this.update(this.value);
24747         }
24748     },
24749
24750     /**
24751      * Gets the current selected value of the date field
24752      * @return {Date} The selected date
24753      */
24754     getValue : function(){
24755         return this.value;
24756     },
24757
24758     // private
24759     focus : function(){
24760         if(this.el){
24761             this.update(this.activeDate);
24762         }
24763     },
24764
24765     // private
24766     onRender : function(container, position){
24767         var m = [
24768              '<table cellspacing="0">',
24769                 '<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>',
24770                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24771         var dn = this.dayNames;
24772         for(var i = 0; i < 7; i++){
24773             var d = this.startDay+i;
24774             if(d > 6){
24775                 d = d-7;
24776             }
24777             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24778         }
24779         m[m.length] = "</tr></thead><tbody><tr>";
24780         for(var i = 0; i < 42; i++) {
24781             if(i % 7 == 0 && i != 0){
24782                 m[m.length] = "</tr><tr>";
24783             }
24784             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24785         }
24786         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24787             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24788
24789         var el = document.createElement("div");
24790         el.className = "x-date-picker";
24791         el.innerHTML = m.join("");
24792
24793         container.dom.insertBefore(el, position);
24794
24795         this.el = Roo.get(el);
24796         this.eventEl = Roo.get(el.firstChild);
24797
24798         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24799             handler: this.showPrevMonth,
24800             scope: this,
24801             preventDefault:true,
24802             stopDefault:true
24803         });
24804
24805         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24806             handler: this.showNextMonth,
24807             scope: this,
24808             preventDefault:true,
24809             stopDefault:true
24810         });
24811
24812         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24813
24814         this.monthPicker = this.el.down('div.x-date-mp');
24815         this.monthPicker.enableDisplayMode('block');
24816         
24817         var kn = new Roo.KeyNav(this.eventEl, {
24818             "left" : function(e){
24819                 e.ctrlKey ?
24820                     this.showPrevMonth() :
24821                     this.update(this.activeDate.add("d", -1));
24822             },
24823
24824             "right" : function(e){
24825                 e.ctrlKey ?
24826                     this.showNextMonth() :
24827                     this.update(this.activeDate.add("d", 1));
24828             },
24829
24830             "up" : function(e){
24831                 e.ctrlKey ?
24832                     this.showNextYear() :
24833                     this.update(this.activeDate.add("d", -7));
24834             },
24835
24836             "down" : function(e){
24837                 e.ctrlKey ?
24838                     this.showPrevYear() :
24839                     this.update(this.activeDate.add("d", 7));
24840             },
24841
24842             "pageUp" : function(e){
24843                 this.showNextMonth();
24844             },
24845
24846             "pageDown" : function(e){
24847                 this.showPrevMonth();
24848             },
24849
24850             "enter" : function(e){
24851                 e.stopPropagation();
24852                 return true;
24853             },
24854
24855             scope : this
24856         });
24857
24858         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24859
24860         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24861
24862         this.el.unselectable();
24863         
24864         this.cells = this.el.select("table.x-date-inner tbody td");
24865         this.textNodes = this.el.query("table.x-date-inner tbody span");
24866
24867         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24868             text: "&#160;",
24869             tooltip: this.monthYearText
24870         });
24871
24872         this.mbtn.on('click', this.showMonthPicker, this);
24873         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24874
24875
24876         var today = (new Date()).dateFormat(this.format);
24877         
24878         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24879         if (this.showClear) {
24880             baseTb.add( new Roo.Toolbar.Fill());
24881         }
24882         baseTb.add({
24883             text: String.format(this.todayText, today),
24884             tooltip: String.format(this.todayTip, today),
24885             handler: this.selectToday,
24886             scope: this
24887         });
24888         
24889         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24890             
24891         //});
24892         if (this.showClear) {
24893             
24894             baseTb.add( new Roo.Toolbar.Fill());
24895             baseTb.add({
24896                 text: '&#160;',
24897                 cls: 'x-btn-icon x-btn-clear',
24898                 handler: function() {
24899                     //this.value = '';
24900                     this.fireEvent("select", this, '');
24901                 },
24902                 scope: this
24903             });
24904         }
24905         
24906         
24907         if(Roo.isIE){
24908             this.el.repaint();
24909         }
24910         this.update(this.value);
24911     },
24912
24913     createMonthPicker : function(){
24914         if(!this.monthPicker.dom.firstChild){
24915             var buf = ['<table border="0" cellspacing="0">'];
24916             for(var i = 0; i < 6; i++){
24917                 buf.push(
24918                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24919                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24920                     i == 0 ?
24921                     '<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>' :
24922                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24923                 );
24924             }
24925             buf.push(
24926                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24927                     this.okText,
24928                     '</button><button type="button" class="x-date-mp-cancel">',
24929                     this.cancelText,
24930                     '</button></td></tr>',
24931                 '</table>'
24932             );
24933             this.monthPicker.update(buf.join(''));
24934             this.monthPicker.on('click', this.onMonthClick, this);
24935             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24936
24937             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24938             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24939
24940             this.mpMonths.each(function(m, a, i){
24941                 i += 1;
24942                 if((i%2) == 0){
24943                     m.dom.xmonth = 5 + Math.round(i * .5);
24944                 }else{
24945                     m.dom.xmonth = Math.round((i-1) * .5);
24946                 }
24947             });
24948         }
24949     },
24950
24951     showMonthPicker : function(){
24952         this.createMonthPicker();
24953         var size = this.el.getSize();
24954         this.monthPicker.setSize(size);
24955         this.monthPicker.child('table').setSize(size);
24956
24957         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24958         this.updateMPMonth(this.mpSelMonth);
24959         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24960         this.updateMPYear(this.mpSelYear);
24961
24962         this.monthPicker.slideIn('t', {duration:.2});
24963     },
24964
24965     updateMPYear : function(y){
24966         this.mpyear = y;
24967         var ys = this.mpYears.elements;
24968         for(var i = 1; i <= 10; i++){
24969             var td = ys[i-1], y2;
24970             if((i%2) == 0){
24971                 y2 = y + Math.round(i * .5);
24972                 td.firstChild.innerHTML = y2;
24973                 td.xyear = y2;
24974             }else{
24975                 y2 = y - (5-Math.round(i * .5));
24976                 td.firstChild.innerHTML = y2;
24977                 td.xyear = y2;
24978             }
24979             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24980         }
24981     },
24982
24983     updateMPMonth : function(sm){
24984         this.mpMonths.each(function(m, a, i){
24985             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24986         });
24987     },
24988
24989     selectMPMonth: function(m){
24990         
24991     },
24992
24993     onMonthClick : function(e, t){
24994         e.stopEvent();
24995         var el = new Roo.Element(t), pn;
24996         if(el.is('button.x-date-mp-cancel')){
24997             this.hideMonthPicker();
24998         }
24999         else if(el.is('button.x-date-mp-ok')){
25000             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25001             this.hideMonthPicker();
25002         }
25003         else if(pn = el.up('td.x-date-mp-month', 2)){
25004             this.mpMonths.removeClass('x-date-mp-sel');
25005             pn.addClass('x-date-mp-sel');
25006             this.mpSelMonth = pn.dom.xmonth;
25007         }
25008         else if(pn = el.up('td.x-date-mp-year', 2)){
25009             this.mpYears.removeClass('x-date-mp-sel');
25010             pn.addClass('x-date-mp-sel');
25011             this.mpSelYear = pn.dom.xyear;
25012         }
25013         else if(el.is('a.x-date-mp-prev')){
25014             this.updateMPYear(this.mpyear-10);
25015         }
25016         else if(el.is('a.x-date-mp-next')){
25017             this.updateMPYear(this.mpyear+10);
25018         }
25019     },
25020
25021     onMonthDblClick : function(e, t){
25022         e.stopEvent();
25023         var el = new Roo.Element(t), pn;
25024         if(pn = el.up('td.x-date-mp-month', 2)){
25025             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25026             this.hideMonthPicker();
25027         }
25028         else if(pn = el.up('td.x-date-mp-year', 2)){
25029             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25030             this.hideMonthPicker();
25031         }
25032     },
25033
25034     hideMonthPicker : function(disableAnim){
25035         if(this.monthPicker){
25036             if(disableAnim === true){
25037                 this.monthPicker.hide();
25038             }else{
25039                 this.monthPicker.slideOut('t', {duration:.2});
25040             }
25041         }
25042     },
25043
25044     // private
25045     showPrevMonth : function(e){
25046         this.update(this.activeDate.add("mo", -1));
25047     },
25048
25049     // private
25050     showNextMonth : function(e){
25051         this.update(this.activeDate.add("mo", 1));
25052     },
25053
25054     // private
25055     showPrevYear : function(){
25056         this.update(this.activeDate.add("y", -1));
25057     },
25058
25059     // private
25060     showNextYear : function(){
25061         this.update(this.activeDate.add("y", 1));
25062     },
25063
25064     // private
25065     handleMouseWheel : function(e){
25066         var delta = e.getWheelDelta();
25067         if(delta > 0){
25068             this.showPrevMonth();
25069             e.stopEvent();
25070         } else if(delta < 0){
25071             this.showNextMonth();
25072             e.stopEvent();
25073         }
25074     },
25075
25076     // private
25077     handleDateClick : function(e, t){
25078         e.stopEvent();
25079         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25080             this.setValue(new Date(t.dateValue));
25081             this.fireEvent("select", this, this.value);
25082         }
25083     },
25084
25085     // private
25086     selectToday : function(){
25087         this.setValue(new Date().clearTime());
25088         this.fireEvent("select", this, this.value);
25089     },
25090
25091     // private
25092     update : function(date)
25093     {
25094         var vd = this.activeDate;
25095         this.activeDate = date;
25096         if(vd && this.el){
25097             var t = date.getTime();
25098             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25099                 this.cells.removeClass("x-date-selected");
25100                 this.cells.each(function(c){
25101                    if(c.dom.firstChild.dateValue == t){
25102                        c.addClass("x-date-selected");
25103                        setTimeout(function(){
25104                             try{c.dom.firstChild.focus();}catch(e){}
25105                        }, 50);
25106                        return false;
25107                    }
25108                 });
25109                 return;
25110             }
25111         }
25112         
25113         var days = date.getDaysInMonth();
25114         var firstOfMonth = date.getFirstDateOfMonth();
25115         var startingPos = firstOfMonth.getDay()-this.startDay;
25116
25117         if(startingPos <= this.startDay){
25118             startingPos += 7;
25119         }
25120
25121         var pm = date.add("mo", -1);
25122         var prevStart = pm.getDaysInMonth()-startingPos;
25123
25124         var cells = this.cells.elements;
25125         var textEls = this.textNodes;
25126         days += startingPos;
25127
25128         // convert everything to numbers so it's fast
25129         var day = 86400000;
25130         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25131         var today = new Date().clearTime().getTime();
25132         var sel = date.clearTime().getTime();
25133         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25134         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25135         var ddMatch = this.disabledDatesRE;
25136         var ddText = this.disabledDatesText;
25137         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25138         var ddaysText = this.disabledDaysText;
25139         var format = this.format;
25140
25141         var setCellClass = function(cal, cell){
25142             cell.title = "";
25143             var t = d.getTime();
25144             cell.firstChild.dateValue = t;
25145             if(t == today){
25146                 cell.className += " x-date-today";
25147                 cell.title = cal.todayText;
25148             }
25149             if(t == sel){
25150                 cell.className += " x-date-selected";
25151                 setTimeout(function(){
25152                     try{cell.firstChild.focus();}catch(e){}
25153                 }, 50);
25154             }
25155             // disabling
25156             if(t < min) {
25157                 cell.className = " x-date-disabled";
25158                 cell.title = cal.minText;
25159                 return;
25160             }
25161             if(t > max) {
25162                 cell.className = " x-date-disabled";
25163                 cell.title = cal.maxText;
25164                 return;
25165             }
25166             if(ddays){
25167                 if(ddays.indexOf(d.getDay()) != -1){
25168                     cell.title = ddaysText;
25169                     cell.className = " x-date-disabled";
25170                 }
25171             }
25172             if(ddMatch && format){
25173                 var fvalue = d.dateFormat(format);
25174                 if(ddMatch.test(fvalue)){
25175                     cell.title = ddText.replace("%0", fvalue);
25176                     cell.className = " x-date-disabled";
25177                 }
25178             }
25179         };
25180
25181         var i = 0;
25182         for(; i < startingPos; i++) {
25183             textEls[i].innerHTML = (++prevStart);
25184             d.setDate(d.getDate()+1);
25185             cells[i].className = "x-date-prevday";
25186             setCellClass(this, cells[i]);
25187         }
25188         for(; i < days; i++){
25189             intDay = i - startingPos + 1;
25190             textEls[i].innerHTML = (intDay);
25191             d.setDate(d.getDate()+1);
25192             cells[i].className = "x-date-active";
25193             setCellClass(this, cells[i]);
25194         }
25195         var extraDays = 0;
25196         for(; i < 42; i++) {
25197              textEls[i].innerHTML = (++extraDays);
25198              d.setDate(d.getDate()+1);
25199              cells[i].className = "x-date-nextday";
25200              setCellClass(this, cells[i]);
25201         }
25202
25203         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25204         this.fireEvent('monthchange', this, date);
25205         
25206         if(!this.internalRender){
25207             var main = this.el.dom.firstChild;
25208             var w = main.offsetWidth;
25209             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25210             Roo.fly(main).setWidth(w);
25211             this.internalRender = true;
25212             // opera does not respect the auto grow header center column
25213             // then, after it gets a width opera refuses to recalculate
25214             // without a second pass
25215             if(Roo.isOpera && !this.secondPass){
25216                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25217                 this.secondPass = true;
25218                 this.update.defer(10, this, [date]);
25219             }
25220         }
25221         
25222         
25223     }
25224 });        /*
25225  * Based on:
25226  * Ext JS Library 1.1.1
25227  * Copyright(c) 2006-2007, Ext JS, LLC.
25228  *
25229  * Originally Released Under LGPL - original licence link has changed is not relivant.
25230  *
25231  * Fork - LGPL
25232  * <script type="text/javascript">
25233  */
25234 /**
25235  * @class Roo.TabPanel
25236  * @extends Roo.util.Observable
25237  * A lightweight tab container.
25238  * <br><br>
25239  * Usage:
25240  * <pre><code>
25241 // basic tabs 1, built from existing content
25242 var tabs = new Roo.TabPanel("tabs1");
25243 tabs.addTab("script", "View Script");
25244 tabs.addTab("markup", "View Markup");
25245 tabs.activate("script");
25246
25247 // more advanced tabs, built from javascript
25248 var jtabs = new Roo.TabPanel("jtabs");
25249 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25250
25251 // set up the UpdateManager
25252 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25253 var updater = tab2.getUpdateManager();
25254 updater.setDefaultUrl("ajax1.htm");
25255 tab2.on('activate', updater.refresh, updater, true);
25256
25257 // Use setUrl for Ajax loading
25258 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25259 tab3.setUrl("ajax2.htm", null, true);
25260
25261 // Disabled tab
25262 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25263 tab4.disable();
25264
25265 jtabs.activate("jtabs-1");
25266  * </code></pre>
25267  * @constructor
25268  * Create a new TabPanel.
25269  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25270  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25271  */
25272 Roo.TabPanel = function(container, config){
25273     /**
25274     * The container element for this TabPanel.
25275     * @type Roo.Element
25276     */
25277     this.el = Roo.get(container, true);
25278     if(config){
25279         if(typeof config == "boolean"){
25280             this.tabPosition = config ? "bottom" : "top";
25281         }else{
25282             Roo.apply(this, config);
25283         }
25284     }
25285     if(this.tabPosition == "bottom"){
25286         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25287         this.el.addClass("x-tabs-bottom");
25288     }
25289     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25290     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25291     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25292     if(Roo.isIE){
25293         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25294     }
25295     if(this.tabPosition != "bottom"){
25296         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25297          * @type Roo.Element
25298          */
25299         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25300         this.el.addClass("x-tabs-top");
25301     }
25302     this.items = [];
25303
25304     this.bodyEl.setStyle("position", "relative");
25305
25306     this.active = null;
25307     this.activateDelegate = this.activate.createDelegate(this);
25308
25309     this.addEvents({
25310         /**
25311          * @event tabchange
25312          * Fires when the active tab changes
25313          * @param {Roo.TabPanel} this
25314          * @param {Roo.TabPanelItem} activePanel The new active tab
25315          */
25316         "tabchange": true,
25317         /**
25318          * @event beforetabchange
25319          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25320          * @param {Roo.TabPanel} this
25321          * @param {Object} e Set cancel to true on this object to cancel the tab change
25322          * @param {Roo.TabPanelItem} tab The tab being changed to
25323          */
25324         "beforetabchange" : true
25325     });
25326
25327     Roo.EventManager.onWindowResize(this.onResize, this);
25328     this.cpad = this.el.getPadding("lr");
25329     this.hiddenCount = 0;
25330
25331
25332     // toolbar on the tabbar support...
25333     if (this.toolbar) {
25334         var tcfg = this.toolbar;
25335         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25336         this.toolbar = new Roo.Toolbar(tcfg);
25337         if (Roo.isSafari) {
25338             var tbl = tcfg.container.child('table', true);
25339             tbl.setAttribute('width', '100%');
25340         }
25341         
25342     }
25343    
25344
25345
25346     Roo.TabPanel.superclass.constructor.call(this);
25347 };
25348
25349 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25350     /*
25351      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25352      */
25353     tabPosition : "top",
25354     /*
25355      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25356      */
25357     currentTabWidth : 0,
25358     /*
25359      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25360      */
25361     minTabWidth : 40,
25362     /*
25363      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25364      */
25365     maxTabWidth : 250,
25366     /*
25367      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25368      */
25369     preferredTabWidth : 175,
25370     /*
25371      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25372      */
25373     resizeTabs : false,
25374     /*
25375      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25376      */
25377     monitorResize : true,
25378     /*
25379      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25380      */
25381     toolbar : false,
25382
25383     /**
25384      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25385      * @param {String} id The id of the div to use <b>or create</b>
25386      * @param {String} text The text for the tab
25387      * @param {String} content (optional) Content to put in the TabPanelItem body
25388      * @param {Boolean} closable (optional) True to create a close icon on the tab
25389      * @return {Roo.TabPanelItem} The created TabPanelItem
25390      */
25391     addTab : function(id, text, content, closable){
25392         var item = new Roo.TabPanelItem(this, id, text, closable);
25393         this.addTabItem(item);
25394         if(content){
25395             item.setContent(content);
25396         }
25397         return item;
25398     },
25399
25400     /**
25401      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25402      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25403      * @return {Roo.TabPanelItem}
25404      */
25405     getTab : function(id){
25406         return this.items[id];
25407     },
25408
25409     /**
25410      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25411      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25412      */
25413     hideTab : function(id){
25414         var t = this.items[id];
25415         if(!t.isHidden()){
25416            t.setHidden(true);
25417            this.hiddenCount++;
25418            this.autoSizeTabs();
25419         }
25420     },
25421
25422     /**
25423      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25424      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25425      */
25426     unhideTab : function(id){
25427         var t = this.items[id];
25428         if(t.isHidden()){
25429            t.setHidden(false);
25430            this.hiddenCount--;
25431            this.autoSizeTabs();
25432         }
25433     },
25434
25435     /**
25436      * Adds an existing {@link Roo.TabPanelItem}.
25437      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25438      */
25439     addTabItem : function(item){
25440         this.items[item.id] = item;
25441         this.items.push(item);
25442         if(this.resizeTabs){
25443            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25444            this.autoSizeTabs();
25445         }else{
25446             item.autoSize();
25447         }
25448     },
25449
25450     /**
25451      * Removes a {@link Roo.TabPanelItem}.
25452      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25453      */
25454     removeTab : function(id){
25455         var items = this.items;
25456         var tab = items[id];
25457         if(!tab) { return; }
25458         var index = items.indexOf(tab);
25459         if(this.active == tab && items.length > 1){
25460             var newTab = this.getNextAvailable(index);
25461             if(newTab) {
25462                 newTab.activate();
25463             }
25464         }
25465         this.stripEl.dom.removeChild(tab.pnode.dom);
25466         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25467             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25468         }
25469         items.splice(index, 1);
25470         delete this.items[tab.id];
25471         tab.fireEvent("close", tab);
25472         tab.purgeListeners();
25473         this.autoSizeTabs();
25474     },
25475
25476     getNextAvailable : function(start){
25477         var items = this.items;
25478         var index = start;
25479         // look for a next tab that will slide over to
25480         // replace the one being removed
25481         while(index < items.length){
25482             var item = items[++index];
25483             if(item && !item.isHidden()){
25484                 return item;
25485             }
25486         }
25487         // if one isn't found select the previous tab (on the left)
25488         index = start;
25489         while(index >= 0){
25490             var item = items[--index];
25491             if(item && !item.isHidden()){
25492                 return item;
25493             }
25494         }
25495         return null;
25496     },
25497
25498     /**
25499      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25500      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25501      */
25502     disableTab : function(id){
25503         var tab = this.items[id];
25504         if(tab && this.active != tab){
25505             tab.disable();
25506         }
25507     },
25508
25509     /**
25510      * Enables a {@link Roo.TabPanelItem} that is disabled.
25511      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25512      */
25513     enableTab : function(id){
25514         var tab = this.items[id];
25515         tab.enable();
25516     },
25517
25518     /**
25519      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25520      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25521      * @return {Roo.TabPanelItem} The TabPanelItem.
25522      */
25523     activate : function(id){
25524         var tab = this.items[id];
25525         if(!tab){
25526             return null;
25527         }
25528         if(tab == this.active || tab.disabled){
25529             return tab;
25530         }
25531         var e = {};
25532         this.fireEvent("beforetabchange", this, e, tab);
25533         if(e.cancel !== true && !tab.disabled){
25534             if(this.active){
25535                 this.active.hide();
25536             }
25537             this.active = this.items[id];
25538             this.active.show();
25539             this.fireEvent("tabchange", this, this.active);
25540         }
25541         return tab;
25542     },
25543
25544     /**
25545      * Gets the active {@link Roo.TabPanelItem}.
25546      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25547      */
25548     getActiveTab : function(){
25549         return this.active;
25550     },
25551
25552     /**
25553      * Updates the tab body element to fit the height of the container element
25554      * for overflow scrolling
25555      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25556      */
25557     syncHeight : function(targetHeight){
25558         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25559         var bm = this.bodyEl.getMargins();
25560         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25561         this.bodyEl.setHeight(newHeight);
25562         return newHeight;
25563     },
25564
25565     onResize : function(){
25566         if(this.monitorResize){
25567             this.autoSizeTabs();
25568         }
25569     },
25570
25571     /**
25572      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25573      */
25574     beginUpdate : function(){
25575         this.updating = true;
25576     },
25577
25578     /**
25579      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25580      */
25581     endUpdate : function(){
25582         this.updating = false;
25583         this.autoSizeTabs();
25584     },
25585
25586     /**
25587      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25588      */
25589     autoSizeTabs : function(){
25590         var count = this.items.length;
25591         var vcount = count - this.hiddenCount;
25592         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25593         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25594         var availWidth = Math.floor(w / vcount);
25595         var b = this.stripBody;
25596         if(b.getWidth() > w){
25597             var tabs = this.items;
25598             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25599             if(availWidth < this.minTabWidth){
25600                 /*if(!this.sleft){    // incomplete scrolling code
25601                     this.createScrollButtons();
25602                 }
25603                 this.showScroll();
25604                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25605             }
25606         }else{
25607             if(this.currentTabWidth < this.preferredTabWidth){
25608                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25609             }
25610         }
25611     },
25612
25613     /**
25614      * Returns the number of tabs in this TabPanel.
25615      * @return {Number}
25616      */
25617      getCount : function(){
25618          return this.items.length;
25619      },
25620
25621     /**
25622      * Resizes all the tabs to the passed width
25623      * @param {Number} The new width
25624      */
25625     setTabWidth : function(width){
25626         this.currentTabWidth = width;
25627         for(var i = 0, len = this.items.length; i < len; i++) {
25628                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25629         }
25630     },
25631
25632     /**
25633      * Destroys this TabPanel
25634      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25635      */
25636     destroy : function(removeEl){
25637         Roo.EventManager.removeResizeListener(this.onResize, this);
25638         for(var i = 0, len = this.items.length; i < len; i++){
25639             this.items[i].purgeListeners();
25640         }
25641         if(removeEl === true){
25642             this.el.update("");
25643             this.el.remove();
25644         }
25645     }
25646 });
25647
25648 /**
25649  * @class Roo.TabPanelItem
25650  * @extends Roo.util.Observable
25651  * Represents an individual item (tab plus body) in a TabPanel.
25652  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25653  * @param {String} id The id of this TabPanelItem
25654  * @param {String} text The text for the tab of this TabPanelItem
25655  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25656  */
25657 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25658     /**
25659      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25660      * @type Roo.TabPanel
25661      */
25662     this.tabPanel = tabPanel;
25663     /**
25664      * The id for this TabPanelItem
25665      * @type String
25666      */
25667     this.id = id;
25668     /** @private */
25669     this.disabled = false;
25670     /** @private */
25671     this.text = text;
25672     /** @private */
25673     this.loaded = false;
25674     this.closable = closable;
25675
25676     /**
25677      * The body element for this TabPanelItem.
25678      * @type Roo.Element
25679      */
25680     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25681     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25682     this.bodyEl.setStyle("display", "block");
25683     this.bodyEl.setStyle("zoom", "1");
25684     this.hideAction();
25685
25686     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25687     /** @private */
25688     this.el = Roo.get(els.el, true);
25689     this.inner = Roo.get(els.inner, true);
25690     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25691     this.pnode = Roo.get(els.el.parentNode, true);
25692     this.el.on("mousedown", this.onTabMouseDown, this);
25693     this.el.on("click", this.onTabClick, this);
25694     /** @private */
25695     if(closable){
25696         var c = Roo.get(els.close, true);
25697         c.dom.title = this.closeText;
25698         c.addClassOnOver("close-over");
25699         c.on("click", this.closeClick, this);
25700      }
25701
25702     this.addEvents({
25703          /**
25704          * @event activate
25705          * Fires when this tab becomes the active tab.
25706          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25707          * @param {Roo.TabPanelItem} this
25708          */
25709         "activate": true,
25710         /**
25711          * @event beforeclose
25712          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25713          * @param {Roo.TabPanelItem} this
25714          * @param {Object} e Set cancel to true on this object to cancel the close.
25715          */
25716         "beforeclose": true,
25717         /**
25718          * @event close
25719          * Fires when this tab is closed.
25720          * @param {Roo.TabPanelItem} this
25721          */
25722          "close": true,
25723         /**
25724          * @event deactivate
25725          * Fires when this tab is no longer the active tab.
25726          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25727          * @param {Roo.TabPanelItem} this
25728          */
25729          "deactivate" : true
25730     });
25731     this.hidden = false;
25732
25733     Roo.TabPanelItem.superclass.constructor.call(this);
25734 };
25735
25736 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25737     purgeListeners : function(){
25738        Roo.util.Observable.prototype.purgeListeners.call(this);
25739        this.el.removeAllListeners();
25740     },
25741     /**
25742      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25743      */
25744     show : function(){
25745         this.pnode.addClass("on");
25746         this.showAction();
25747         if(Roo.isOpera){
25748             this.tabPanel.stripWrap.repaint();
25749         }
25750         this.fireEvent("activate", this.tabPanel, this);
25751     },
25752
25753     /**
25754      * Returns true if this tab is the active tab.
25755      * @return {Boolean}
25756      */
25757     isActive : function(){
25758         return this.tabPanel.getActiveTab() == this;
25759     },
25760
25761     /**
25762      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25763      */
25764     hide : function(){
25765         this.pnode.removeClass("on");
25766         this.hideAction();
25767         this.fireEvent("deactivate", this.tabPanel, this);
25768     },
25769
25770     hideAction : function(){
25771         this.bodyEl.hide();
25772         this.bodyEl.setStyle("position", "absolute");
25773         this.bodyEl.setLeft("-20000px");
25774         this.bodyEl.setTop("-20000px");
25775     },
25776
25777     showAction : function(){
25778         this.bodyEl.setStyle("position", "relative");
25779         this.bodyEl.setTop("");
25780         this.bodyEl.setLeft("");
25781         this.bodyEl.show();
25782     },
25783
25784     /**
25785      * Set the tooltip for the tab.
25786      * @param {String} tooltip The tab's tooltip
25787      */
25788     setTooltip : function(text){
25789         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25790             this.textEl.dom.qtip = text;
25791             this.textEl.dom.removeAttribute('title');
25792         }else{
25793             this.textEl.dom.title = text;
25794         }
25795     },
25796
25797     onTabClick : function(e){
25798         e.preventDefault();
25799         this.tabPanel.activate(this.id);
25800     },
25801
25802     onTabMouseDown : function(e){
25803         e.preventDefault();
25804         this.tabPanel.activate(this.id);
25805     },
25806
25807     getWidth : function(){
25808         return this.inner.getWidth();
25809     },
25810
25811     setWidth : function(width){
25812         var iwidth = width - this.pnode.getPadding("lr");
25813         this.inner.setWidth(iwidth);
25814         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25815         this.pnode.setWidth(width);
25816     },
25817
25818     /**
25819      * Show or hide the tab
25820      * @param {Boolean} hidden True to hide or false to show.
25821      */
25822     setHidden : function(hidden){
25823         this.hidden = hidden;
25824         this.pnode.setStyle("display", hidden ? "none" : "");
25825     },
25826
25827     /**
25828      * Returns true if this tab is "hidden"
25829      * @return {Boolean}
25830      */
25831     isHidden : function(){
25832         return this.hidden;
25833     },
25834
25835     /**
25836      * Returns the text for this tab
25837      * @return {String}
25838      */
25839     getText : function(){
25840         return this.text;
25841     },
25842
25843     autoSize : function(){
25844         //this.el.beginMeasure();
25845         this.textEl.setWidth(1);
25846         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25847         //this.el.endMeasure();
25848     },
25849
25850     /**
25851      * Sets the text for the tab (Note: this also sets the tooltip text)
25852      * @param {String} text The tab's text and tooltip
25853      */
25854     setText : function(text){
25855         this.text = text;
25856         this.textEl.update(text);
25857         this.setTooltip(text);
25858         if(!this.tabPanel.resizeTabs){
25859             this.autoSize();
25860         }
25861     },
25862     /**
25863      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25864      */
25865     activate : function(){
25866         this.tabPanel.activate(this.id);
25867     },
25868
25869     /**
25870      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25871      */
25872     disable : function(){
25873         if(this.tabPanel.active != this){
25874             this.disabled = true;
25875             this.pnode.addClass("disabled");
25876         }
25877     },
25878
25879     /**
25880      * Enables this TabPanelItem if it was previously disabled.
25881      */
25882     enable : function(){
25883         this.disabled = false;
25884         this.pnode.removeClass("disabled");
25885     },
25886
25887     /**
25888      * Sets the content for this TabPanelItem.
25889      * @param {String} content The content
25890      * @param {Boolean} loadScripts true to look for and load scripts
25891      */
25892     setContent : function(content, loadScripts){
25893         this.bodyEl.update(content, loadScripts);
25894     },
25895
25896     /**
25897      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25898      * @return {Roo.UpdateManager} The UpdateManager
25899      */
25900     getUpdateManager : function(){
25901         return this.bodyEl.getUpdateManager();
25902     },
25903
25904     /**
25905      * Set a URL to be used to load the content for this TabPanelItem.
25906      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25907      * @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)
25908      * @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)
25909      * @return {Roo.UpdateManager} The UpdateManager
25910      */
25911     setUrl : function(url, params, loadOnce){
25912         if(this.refreshDelegate){
25913             this.un('activate', this.refreshDelegate);
25914         }
25915         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25916         this.on("activate", this.refreshDelegate);
25917         return this.bodyEl.getUpdateManager();
25918     },
25919
25920     /** @private */
25921     _handleRefresh : function(url, params, loadOnce){
25922         if(!loadOnce || !this.loaded){
25923             var updater = this.bodyEl.getUpdateManager();
25924             updater.update(url, params, this._setLoaded.createDelegate(this));
25925         }
25926     },
25927
25928     /**
25929      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25930      *   Will fail silently if the setUrl method has not been called.
25931      *   This does not activate the panel, just updates its content.
25932      */
25933     refresh : function(){
25934         if(this.refreshDelegate){
25935            this.loaded = false;
25936            this.refreshDelegate();
25937         }
25938     },
25939
25940     /** @private */
25941     _setLoaded : function(){
25942         this.loaded = true;
25943     },
25944
25945     /** @private */
25946     closeClick : function(e){
25947         var o = {};
25948         e.stopEvent();
25949         this.fireEvent("beforeclose", this, o);
25950         if(o.cancel !== true){
25951             this.tabPanel.removeTab(this.id);
25952         }
25953     },
25954     /**
25955      * The text displayed in the tooltip for the close icon.
25956      * @type String
25957      */
25958     closeText : "Close this tab"
25959 });
25960
25961 /** @private */
25962 Roo.TabPanel.prototype.createStrip = function(container){
25963     var strip = document.createElement("div");
25964     strip.className = "x-tabs-wrap";
25965     container.appendChild(strip);
25966     return strip;
25967 };
25968 /** @private */
25969 Roo.TabPanel.prototype.createStripList = function(strip){
25970     // div wrapper for retard IE
25971     // returns the "tr" element.
25972     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25973         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25974         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25975     return strip.firstChild.firstChild.firstChild.firstChild;
25976 };
25977 /** @private */
25978 Roo.TabPanel.prototype.createBody = function(container){
25979     var body = document.createElement("div");
25980     Roo.id(body, "tab-body");
25981     Roo.fly(body).addClass("x-tabs-body");
25982     container.appendChild(body);
25983     return body;
25984 };
25985 /** @private */
25986 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25987     var body = Roo.getDom(id);
25988     if(!body){
25989         body = document.createElement("div");
25990         body.id = id;
25991     }
25992     Roo.fly(body).addClass("x-tabs-item-body");
25993     bodyEl.insertBefore(body, bodyEl.firstChild);
25994     return body;
25995 };
25996 /** @private */
25997 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25998     var td = document.createElement("td");
25999     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26000     //stripEl.appendChild(td);
26001     if(closable){
26002         td.className = "x-tabs-closable";
26003         if(!this.closeTpl){
26004             this.closeTpl = new Roo.Template(
26005                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26006                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26007                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26008             );
26009         }
26010         var el = this.closeTpl.overwrite(td, {"text": text});
26011         var close = el.getElementsByTagName("div")[0];
26012         var inner = el.getElementsByTagName("em")[0];
26013         return {"el": el, "close": close, "inner": inner};
26014     } else {
26015         if(!this.tabTpl){
26016             this.tabTpl = new Roo.Template(
26017                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26018                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26019             );
26020         }
26021         var el = this.tabTpl.overwrite(td, {"text": text});
26022         var inner = el.getElementsByTagName("em")[0];
26023         return {"el": el, "inner": inner};
26024     }
26025 };/*
26026  * Based on:
26027  * Ext JS Library 1.1.1
26028  * Copyright(c) 2006-2007, Ext JS, LLC.
26029  *
26030  * Originally Released Under LGPL - original licence link has changed is not relivant.
26031  *
26032  * Fork - LGPL
26033  * <script type="text/javascript">
26034  */
26035
26036 /**
26037  * @class Roo.Button
26038  * @extends Roo.util.Observable
26039  * Simple Button class
26040  * @cfg {String} text The button text
26041  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26042  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26043  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26044  * @cfg {Object} scope The scope of the handler
26045  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26046  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26047  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26048  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26049  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26050  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26051    applies if enableToggle = true)
26052  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26053  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26054   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26055  * @constructor
26056  * Create a new button
26057  * @param {Object} config The config object
26058  */
26059 Roo.Button = function(renderTo, config)
26060 {
26061     if (!config) {
26062         config = renderTo;
26063         renderTo = config.renderTo || false;
26064     }
26065     
26066     Roo.apply(this, config);
26067     this.addEvents({
26068         /**
26069              * @event click
26070              * Fires when this button is clicked
26071              * @param {Button} this
26072              * @param {EventObject} e The click event
26073              */
26074             "click" : true,
26075         /**
26076              * @event toggle
26077              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26078              * @param {Button} this
26079              * @param {Boolean} pressed
26080              */
26081             "toggle" : true,
26082         /**
26083              * @event mouseover
26084              * Fires when the mouse hovers over the button
26085              * @param {Button} this
26086              * @param {Event} e The event object
26087              */
26088         'mouseover' : true,
26089         /**
26090              * @event mouseout
26091              * Fires when the mouse exits the button
26092              * @param {Button} this
26093              * @param {Event} e The event object
26094              */
26095         'mouseout': true,
26096          /**
26097              * @event render
26098              * Fires when the button is rendered
26099              * @param {Button} this
26100              */
26101         'render': true
26102     });
26103     if(this.menu){
26104         this.menu = Roo.menu.MenuMgr.get(this.menu);
26105     }
26106     // register listeners first!!  - so render can be captured..
26107     Roo.util.Observable.call(this);
26108     if(renderTo){
26109         this.render(renderTo);
26110     }
26111     
26112   
26113 };
26114
26115 Roo.extend(Roo.Button, Roo.util.Observable, {
26116     /**
26117      * 
26118      */
26119     
26120     /**
26121      * Read-only. True if this button is hidden
26122      * @type Boolean
26123      */
26124     hidden : false,
26125     /**
26126      * Read-only. True if this button is disabled
26127      * @type Boolean
26128      */
26129     disabled : false,
26130     /**
26131      * Read-only. True if this button is pressed (only if enableToggle = true)
26132      * @type Boolean
26133      */
26134     pressed : false,
26135
26136     /**
26137      * @cfg {Number} tabIndex 
26138      * The DOM tabIndex for this button (defaults to undefined)
26139      */
26140     tabIndex : undefined,
26141
26142     /**
26143      * @cfg {Boolean} enableToggle
26144      * True to enable pressed/not pressed toggling (defaults to false)
26145      */
26146     enableToggle: false,
26147     /**
26148      * @cfg {Mixed} menu
26149      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26150      */
26151     menu : undefined,
26152     /**
26153      * @cfg {String} menuAlign
26154      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26155      */
26156     menuAlign : "tl-bl?",
26157
26158     /**
26159      * @cfg {String} iconCls
26160      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26161      */
26162     iconCls : undefined,
26163     /**
26164      * @cfg {String} type
26165      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26166      */
26167     type : 'button',
26168
26169     // private
26170     menuClassTarget: 'tr',
26171
26172     /**
26173      * @cfg {String} clickEvent
26174      * The type of event to map to the button's event handler (defaults to 'click')
26175      */
26176     clickEvent : 'click',
26177
26178     /**
26179      * @cfg {Boolean} handleMouseEvents
26180      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26181      */
26182     handleMouseEvents : true,
26183
26184     /**
26185      * @cfg {String} tooltipType
26186      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26187      */
26188     tooltipType : 'qtip',
26189
26190     /**
26191      * @cfg {String} cls
26192      * A CSS class to apply to the button's main element.
26193      */
26194     
26195     /**
26196      * @cfg {Roo.Template} template (Optional)
26197      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26198      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26199      * require code modifications if required elements (e.g. a button) aren't present.
26200      */
26201
26202     // private
26203     render : function(renderTo){
26204         var btn;
26205         if(this.hideParent){
26206             this.parentEl = Roo.get(renderTo);
26207         }
26208         if(!this.dhconfig){
26209             if(!this.template){
26210                 if(!Roo.Button.buttonTemplate){
26211                     // hideous table template
26212                     Roo.Button.buttonTemplate = new Roo.Template(
26213                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26214                         '<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>',
26215                         "</tr></tbody></table>");
26216                 }
26217                 this.template = Roo.Button.buttonTemplate;
26218             }
26219             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26220             var btnEl = btn.child("button:first");
26221             btnEl.on('focus', this.onFocus, this);
26222             btnEl.on('blur', this.onBlur, this);
26223             if(this.cls){
26224                 btn.addClass(this.cls);
26225             }
26226             if(this.icon){
26227                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26228             }
26229             if(this.iconCls){
26230                 btnEl.addClass(this.iconCls);
26231                 if(!this.cls){
26232                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26233                 }
26234             }
26235             if(this.tabIndex !== undefined){
26236                 btnEl.dom.tabIndex = this.tabIndex;
26237             }
26238             if(this.tooltip){
26239                 if(typeof this.tooltip == 'object'){
26240                     Roo.QuickTips.tips(Roo.apply({
26241                           target: btnEl.id
26242                     }, this.tooltip));
26243                 } else {
26244                     btnEl.dom[this.tooltipType] = this.tooltip;
26245                 }
26246             }
26247         }else{
26248             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26249         }
26250         this.el = btn;
26251         if(this.id){
26252             this.el.dom.id = this.el.id = this.id;
26253         }
26254         if(this.menu){
26255             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26256             this.menu.on("show", this.onMenuShow, this);
26257             this.menu.on("hide", this.onMenuHide, this);
26258         }
26259         btn.addClass("x-btn");
26260         if(Roo.isIE && !Roo.isIE7){
26261             this.autoWidth.defer(1, this);
26262         }else{
26263             this.autoWidth();
26264         }
26265         if(this.handleMouseEvents){
26266             btn.on("mouseover", this.onMouseOver, this);
26267             btn.on("mouseout", this.onMouseOut, this);
26268             btn.on("mousedown", this.onMouseDown, this);
26269         }
26270         btn.on(this.clickEvent, this.onClick, this);
26271         //btn.on("mouseup", this.onMouseUp, this);
26272         if(this.hidden){
26273             this.hide();
26274         }
26275         if(this.disabled){
26276             this.disable();
26277         }
26278         Roo.ButtonToggleMgr.register(this);
26279         if(this.pressed){
26280             this.el.addClass("x-btn-pressed");
26281         }
26282         if(this.repeat){
26283             var repeater = new Roo.util.ClickRepeater(btn,
26284                 typeof this.repeat == "object" ? this.repeat : {}
26285             );
26286             repeater.on("click", this.onClick,  this);
26287         }
26288         
26289         this.fireEvent('render', this);
26290         
26291     },
26292     /**
26293      * Returns the button's underlying element
26294      * @return {Roo.Element} The element
26295      */
26296     getEl : function(){
26297         return this.el;  
26298     },
26299     
26300     /**
26301      * Destroys this Button and removes any listeners.
26302      */
26303     destroy : function(){
26304         Roo.ButtonToggleMgr.unregister(this);
26305         this.el.removeAllListeners();
26306         this.purgeListeners();
26307         this.el.remove();
26308     },
26309
26310     // private
26311     autoWidth : function(){
26312         if(this.el){
26313             this.el.setWidth("auto");
26314             if(Roo.isIE7 && Roo.isStrict){
26315                 var ib = this.el.child('button');
26316                 if(ib && ib.getWidth() > 20){
26317                     ib.clip();
26318                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26319                 }
26320             }
26321             if(this.minWidth){
26322                 if(this.hidden){
26323                     this.el.beginMeasure();
26324                 }
26325                 if(this.el.getWidth() < this.minWidth){
26326                     this.el.setWidth(this.minWidth);
26327                 }
26328                 if(this.hidden){
26329                     this.el.endMeasure();
26330                 }
26331             }
26332         }
26333     },
26334
26335     /**
26336      * Assigns this button's click handler
26337      * @param {Function} handler The function to call when the button is clicked
26338      * @param {Object} scope (optional) Scope for the function passed in
26339      */
26340     setHandler : function(handler, scope){
26341         this.handler = handler;
26342         this.scope = scope;  
26343     },
26344     
26345     /**
26346      * Sets this button's text
26347      * @param {String} text The button text
26348      */
26349     setText : function(text){
26350         this.text = text;
26351         if(this.el){
26352             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26353         }
26354         this.autoWidth();
26355     },
26356     
26357     /**
26358      * Gets the text for this button
26359      * @return {String} The button text
26360      */
26361     getText : function(){
26362         return this.text;  
26363     },
26364     
26365     /**
26366      * Show this button
26367      */
26368     show: function(){
26369         this.hidden = false;
26370         if(this.el){
26371             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26372         }
26373     },
26374     
26375     /**
26376      * Hide this button
26377      */
26378     hide: function(){
26379         this.hidden = true;
26380         if(this.el){
26381             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26382         }
26383     },
26384     
26385     /**
26386      * Convenience function for boolean show/hide
26387      * @param {Boolean} visible True to show, false to hide
26388      */
26389     setVisible: function(visible){
26390         if(visible) {
26391             this.show();
26392         }else{
26393             this.hide();
26394         }
26395     },
26396     
26397     /**
26398      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26399      * @param {Boolean} state (optional) Force a particular state
26400      */
26401     toggle : function(state){
26402         state = state === undefined ? !this.pressed : state;
26403         if(state != this.pressed){
26404             if(state){
26405                 this.el.addClass("x-btn-pressed");
26406                 this.pressed = true;
26407                 this.fireEvent("toggle", this, true);
26408             }else{
26409                 this.el.removeClass("x-btn-pressed");
26410                 this.pressed = false;
26411                 this.fireEvent("toggle", this, false);
26412             }
26413             if(this.toggleHandler){
26414                 this.toggleHandler.call(this.scope || this, this, state);
26415             }
26416         }
26417     },
26418     
26419     /**
26420      * Focus the button
26421      */
26422     focus : function(){
26423         this.el.child('button:first').focus();
26424     },
26425     
26426     /**
26427      * Disable this button
26428      */
26429     disable : function(){
26430         if(this.el){
26431             this.el.addClass("x-btn-disabled");
26432         }
26433         this.disabled = true;
26434     },
26435     
26436     /**
26437      * Enable this button
26438      */
26439     enable : function(){
26440         if(this.el){
26441             this.el.removeClass("x-btn-disabled");
26442         }
26443         this.disabled = false;
26444     },
26445
26446     /**
26447      * Convenience function for boolean enable/disable
26448      * @param {Boolean} enabled True to enable, false to disable
26449      */
26450     setDisabled : function(v){
26451         this[v !== true ? "enable" : "disable"]();
26452     },
26453
26454     // private
26455     onClick : function(e){
26456         if(e){
26457             e.preventDefault();
26458         }
26459         if(e.button != 0){
26460             return;
26461         }
26462         if(!this.disabled){
26463             if(this.enableToggle){
26464                 this.toggle();
26465             }
26466             if(this.menu && !this.menu.isVisible()){
26467                 this.menu.show(this.el, this.menuAlign);
26468             }
26469             this.fireEvent("click", this, e);
26470             if(this.handler){
26471                 this.el.removeClass("x-btn-over");
26472                 this.handler.call(this.scope || this, this, e);
26473             }
26474         }
26475     },
26476     // private
26477     onMouseOver : function(e){
26478         if(!this.disabled){
26479             this.el.addClass("x-btn-over");
26480             this.fireEvent('mouseover', this, e);
26481         }
26482     },
26483     // private
26484     onMouseOut : function(e){
26485         if(!e.within(this.el,  true)){
26486             this.el.removeClass("x-btn-over");
26487             this.fireEvent('mouseout', this, e);
26488         }
26489     },
26490     // private
26491     onFocus : function(e){
26492         if(!this.disabled){
26493             this.el.addClass("x-btn-focus");
26494         }
26495     },
26496     // private
26497     onBlur : function(e){
26498         this.el.removeClass("x-btn-focus");
26499     },
26500     // private
26501     onMouseDown : function(e){
26502         if(!this.disabled && e.button == 0){
26503             this.el.addClass("x-btn-click");
26504             Roo.get(document).on('mouseup', this.onMouseUp, this);
26505         }
26506     },
26507     // private
26508     onMouseUp : function(e){
26509         if(e.button == 0){
26510             this.el.removeClass("x-btn-click");
26511             Roo.get(document).un('mouseup', this.onMouseUp, this);
26512         }
26513     },
26514     // private
26515     onMenuShow : function(e){
26516         this.el.addClass("x-btn-menu-active");
26517     },
26518     // private
26519     onMenuHide : function(e){
26520         this.el.removeClass("x-btn-menu-active");
26521     }   
26522 });
26523
26524 // Private utility class used by Button
26525 Roo.ButtonToggleMgr = function(){
26526    var groups = {};
26527    
26528    function toggleGroup(btn, state){
26529        if(state){
26530            var g = groups[btn.toggleGroup];
26531            for(var i = 0, l = g.length; i < l; i++){
26532                if(g[i] != btn){
26533                    g[i].toggle(false);
26534                }
26535            }
26536        }
26537    }
26538    
26539    return {
26540        register : function(btn){
26541            if(!btn.toggleGroup){
26542                return;
26543            }
26544            var g = groups[btn.toggleGroup];
26545            if(!g){
26546                g = groups[btn.toggleGroup] = [];
26547            }
26548            g.push(btn);
26549            btn.on("toggle", toggleGroup);
26550        },
26551        
26552        unregister : function(btn){
26553            if(!btn.toggleGroup){
26554                return;
26555            }
26556            var g = groups[btn.toggleGroup];
26557            if(g){
26558                g.remove(btn);
26559                btn.un("toggle", toggleGroup);
26560            }
26561        }
26562    };
26563 }();/*
26564  * Based on:
26565  * Ext JS Library 1.1.1
26566  * Copyright(c) 2006-2007, Ext JS, LLC.
26567  *
26568  * Originally Released Under LGPL - original licence link has changed is not relivant.
26569  *
26570  * Fork - LGPL
26571  * <script type="text/javascript">
26572  */
26573  
26574 /**
26575  * @class Roo.SplitButton
26576  * @extends Roo.Button
26577  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26578  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26579  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26580  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26581  * @cfg {String} arrowTooltip The title attribute of the arrow
26582  * @constructor
26583  * Create a new menu button
26584  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26585  * @param {Object} config The config object
26586  */
26587 Roo.SplitButton = function(renderTo, config){
26588     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26589     /**
26590      * @event arrowclick
26591      * Fires when this button's arrow is clicked
26592      * @param {SplitButton} this
26593      * @param {EventObject} e The click event
26594      */
26595     this.addEvents({"arrowclick":true});
26596 };
26597
26598 Roo.extend(Roo.SplitButton, Roo.Button, {
26599     render : function(renderTo){
26600         // this is one sweet looking template!
26601         var tpl = new Roo.Template(
26602             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26603             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26604             '<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>',
26605             "</tbody></table></td><td>",
26606             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26607             '<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>',
26608             "</tbody></table></td></tr></table>"
26609         );
26610         var btn = tpl.append(renderTo, [this.text, this.type], true);
26611         var btnEl = btn.child("button");
26612         if(this.cls){
26613             btn.addClass(this.cls);
26614         }
26615         if(this.icon){
26616             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26617         }
26618         if(this.iconCls){
26619             btnEl.addClass(this.iconCls);
26620             if(!this.cls){
26621                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26622             }
26623         }
26624         this.el = btn;
26625         if(this.handleMouseEvents){
26626             btn.on("mouseover", this.onMouseOver, this);
26627             btn.on("mouseout", this.onMouseOut, this);
26628             btn.on("mousedown", this.onMouseDown, this);
26629             btn.on("mouseup", this.onMouseUp, this);
26630         }
26631         btn.on(this.clickEvent, this.onClick, this);
26632         if(this.tooltip){
26633             if(typeof this.tooltip == 'object'){
26634                 Roo.QuickTips.tips(Roo.apply({
26635                       target: btnEl.id
26636                 }, this.tooltip));
26637             } else {
26638                 btnEl.dom[this.tooltipType] = this.tooltip;
26639             }
26640         }
26641         if(this.arrowTooltip){
26642             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26643         }
26644         if(this.hidden){
26645             this.hide();
26646         }
26647         if(this.disabled){
26648             this.disable();
26649         }
26650         if(this.pressed){
26651             this.el.addClass("x-btn-pressed");
26652         }
26653         if(Roo.isIE && !Roo.isIE7){
26654             this.autoWidth.defer(1, this);
26655         }else{
26656             this.autoWidth();
26657         }
26658         if(this.menu){
26659             this.menu.on("show", this.onMenuShow, this);
26660             this.menu.on("hide", this.onMenuHide, this);
26661         }
26662         this.fireEvent('render', this);
26663     },
26664
26665     // private
26666     autoWidth : function(){
26667         if(this.el){
26668             var tbl = this.el.child("table:first");
26669             var tbl2 = this.el.child("table:last");
26670             this.el.setWidth("auto");
26671             tbl.setWidth("auto");
26672             if(Roo.isIE7 && Roo.isStrict){
26673                 var ib = this.el.child('button:first');
26674                 if(ib && ib.getWidth() > 20){
26675                     ib.clip();
26676                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26677                 }
26678             }
26679             if(this.minWidth){
26680                 if(this.hidden){
26681                     this.el.beginMeasure();
26682                 }
26683                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26684                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26685                 }
26686                 if(this.hidden){
26687                     this.el.endMeasure();
26688                 }
26689             }
26690             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26691         } 
26692     },
26693     /**
26694      * Sets this button's click handler
26695      * @param {Function} handler The function to call when the button is clicked
26696      * @param {Object} scope (optional) Scope for the function passed above
26697      */
26698     setHandler : function(handler, scope){
26699         this.handler = handler;
26700         this.scope = scope;  
26701     },
26702     
26703     /**
26704      * Sets this button's arrow click handler
26705      * @param {Function} handler The function to call when the arrow is clicked
26706      * @param {Object} scope (optional) Scope for the function passed above
26707      */
26708     setArrowHandler : function(handler, scope){
26709         this.arrowHandler = handler;
26710         this.scope = scope;  
26711     },
26712     
26713     /**
26714      * Focus the button
26715      */
26716     focus : function(){
26717         if(this.el){
26718             this.el.child("button:first").focus();
26719         }
26720     },
26721
26722     // private
26723     onClick : function(e){
26724         e.preventDefault();
26725         if(!this.disabled){
26726             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26727                 if(this.menu && !this.menu.isVisible()){
26728                     this.menu.show(this.el, this.menuAlign);
26729                 }
26730                 this.fireEvent("arrowclick", this, e);
26731                 if(this.arrowHandler){
26732                     this.arrowHandler.call(this.scope || this, this, e);
26733                 }
26734             }else{
26735                 this.fireEvent("click", this, e);
26736                 if(this.handler){
26737                     this.handler.call(this.scope || this, this, e);
26738                 }
26739             }
26740         }
26741     },
26742     // private
26743     onMouseDown : function(e){
26744         if(!this.disabled){
26745             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26746         }
26747     },
26748     // private
26749     onMouseUp : function(e){
26750         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26751     }   
26752 });
26753
26754
26755 // backwards compat
26756 Roo.MenuButton = Roo.SplitButton;/*
26757  * Based on:
26758  * Ext JS Library 1.1.1
26759  * Copyright(c) 2006-2007, Ext JS, LLC.
26760  *
26761  * Originally Released Under LGPL - original licence link has changed is not relivant.
26762  *
26763  * Fork - LGPL
26764  * <script type="text/javascript">
26765  */
26766
26767 /**
26768  * @class Roo.Toolbar
26769  * Basic Toolbar class.
26770  * @constructor
26771  * Creates a new Toolbar
26772  * @param {Object} container The config object
26773  */ 
26774 Roo.Toolbar = function(container, buttons, config)
26775 {
26776     /// old consturctor format still supported..
26777     if(container instanceof Array){ // omit the container for later rendering
26778         buttons = container;
26779         config = buttons;
26780         container = null;
26781     }
26782     if (typeof(container) == 'object' && container.xtype) {
26783         config = container;
26784         container = config.container;
26785         buttons = config.buttons || []; // not really - use items!!
26786     }
26787     var xitems = [];
26788     if (config && config.items) {
26789         xitems = config.items;
26790         delete config.items;
26791     }
26792     Roo.apply(this, config);
26793     this.buttons = buttons;
26794     
26795     if(container){
26796         this.render(container);
26797     }
26798     this.xitems = xitems;
26799     Roo.each(xitems, function(b) {
26800         this.add(b);
26801     }, this);
26802     
26803 };
26804
26805 Roo.Toolbar.prototype = {
26806     /**
26807      * @cfg {Array} items
26808      * array of button configs or elements to add (will be converted to a MixedCollection)
26809      */
26810     
26811     /**
26812      * @cfg {String/HTMLElement/Element} container
26813      * The id or element that will contain the toolbar
26814      */
26815     // private
26816     render : function(ct){
26817         this.el = Roo.get(ct);
26818         if(this.cls){
26819             this.el.addClass(this.cls);
26820         }
26821         // using a table allows for vertical alignment
26822         // 100% width is needed by Safari...
26823         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26824         this.tr = this.el.child("tr", true);
26825         var autoId = 0;
26826         this.items = new Roo.util.MixedCollection(false, function(o){
26827             return o.id || ("item" + (++autoId));
26828         });
26829         if(this.buttons){
26830             this.add.apply(this, this.buttons);
26831             delete this.buttons;
26832         }
26833     },
26834
26835     /**
26836      * Adds element(s) to the toolbar -- this function takes a variable number of 
26837      * arguments of mixed type and adds them to the toolbar.
26838      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26839      * <ul>
26840      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26841      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26842      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26843      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26844      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26845      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26846      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26847      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26848      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26849      * </ul>
26850      * @param {Mixed} arg2
26851      * @param {Mixed} etc.
26852      */
26853     add : function(){
26854         var a = arguments, l = a.length;
26855         for(var i = 0; i < l; i++){
26856             this._add(a[i]);
26857         }
26858     },
26859     // private..
26860     _add : function(el) {
26861         
26862         if (el.xtype) {
26863             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26864         }
26865         
26866         if (el.applyTo){ // some kind of form field
26867             return this.addField(el);
26868         } 
26869         if (el.render){ // some kind of Toolbar.Item
26870             return this.addItem(el);
26871         }
26872         if (typeof el == "string"){ // string
26873             if(el == "separator" || el == "-"){
26874                 return this.addSeparator();
26875             }
26876             if (el == " "){
26877                 return this.addSpacer();
26878             }
26879             if(el == "->"){
26880                 return this.addFill();
26881             }
26882             return this.addText(el);
26883             
26884         }
26885         if(el.tagName){ // element
26886             return this.addElement(el);
26887         }
26888         if(typeof el == "object"){ // must be button config?
26889             return this.addButton(el);
26890         }
26891         // and now what?!?!
26892         return false;
26893         
26894     },
26895     
26896     /**
26897      * Add an Xtype element
26898      * @param {Object} xtype Xtype Object
26899      * @return {Object} created Object
26900      */
26901     addxtype : function(e){
26902         return this.add(e);  
26903     },
26904     
26905     /**
26906      * Returns the Element for this toolbar.
26907      * @return {Roo.Element}
26908      */
26909     getEl : function(){
26910         return this.el;  
26911     },
26912     
26913     /**
26914      * Adds a separator
26915      * @return {Roo.Toolbar.Item} The separator item
26916      */
26917     addSeparator : function(){
26918         return this.addItem(new Roo.Toolbar.Separator());
26919     },
26920
26921     /**
26922      * Adds a spacer element
26923      * @return {Roo.Toolbar.Spacer} The spacer item
26924      */
26925     addSpacer : function(){
26926         return this.addItem(new Roo.Toolbar.Spacer());
26927     },
26928
26929     /**
26930      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26931      * @return {Roo.Toolbar.Fill} The fill item
26932      */
26933     addFill : function(){
26934         return this.addItem(new Roo.Toolbar.Fill());
26935     },
26936
26937     /**
26938      * Adds any standard HTML element to the toolbar
26939      * @param {String/HTMLElement/Element} el The element or id of the element to add
26940      * @return {Roo.Toolbar.Item} The element's item
26941      */
26942     addElement : function(el){
26943         return this.addItem(new Roo.Toolbar.Item(el));
26944     },
26945     /**
26946      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26947      * @type Roo.util.MixedCollection  
26948      */
26949     items : false,
26950      
26951     /**
26952      * Adds any Toolbar.Item or subclass
26953      * @param {Roo.Toolbar.Item} item
26954      * @return {Roo.Toolbar.Item} The item
26955      */
26956     addItem : function(item){
26957         var td = this.nextBlock();
26958         item.render(td);
26959         this.items.add(item);
26960         return item;
26961     },
26962     
26963     /**
26964      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26965      * @param {Object/Array} config A button config or array of configs
26966      * @return {Roo.Toolbar.Button/Array}
26967      */
26968     addButton : function(config){
26969         if(config instanceof Array){
26970             var buttons = [];
26971             for(var i = 0, len = config.length; i < len; i++) {
26972                 buttons.push(this.addButton(config[i]));
26973             }
26974             return buttons;
26975         }
26976         var b = config;
26977         if(!(config instanceof Roo.Toolbar.Button)){
26978             b = config.split ?
26979                 new Roo.Toolbar.SplitButton(config) :
26980                 new Roo.Toolbar.Button(config);
26981         }
26982         var td = this.nextBlock();
26983         b.render(td);
26984         this.items.add(b);
26985         return b;
26986     },
26987     
26988     /**
26989      * Adds text to the toolbar
26990      * @param {String} text The text to add
26991      * @return {Roo.Toolbar.Item} The element's item
26992      */
26993     addText : function(text){
26994         return this.addItem(new Roo.Toolbar.TextItem(text));
26995     },
26996     
26997     /**
26998      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26999      * @param {Number} index The index where the item is to be inserted
27000      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27001      * @return {Roo.Toolbar.Button/Item}
27002      */
27003     insertButton : function(index, item){
27004         if(item instanceof Array){
27005             var buttons = [];
27006             for(var i = 0, len = item.length; i < len; i++) {
27007                buttons.push(this.insertButton(index + i, item[i]));
27008             }
27009             return buttons;
27010         }
27011         if (!(item instanceof Roo.Toolbar.Button)){
27012            item = new Roo.Toolbar.Button(item);
27013         }
27014         var td = document.createElement("td");
27015         this.tr.insertBefore(td, this.tr.childNodes[index]);
27016         item.render(td);
27017         this.items.insert(index, item);
27018         return item;
27019     },
27020     
27021     /**
27022      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27023      * @param {Object} config
27024      * @return {Roo.Toolbar.Item} The element's item
27025      */
27026     addDom : function(config, returnEl){
27027         var td = this.nextBlock();
27028         Roo.DomHelper.overwrite(td, config);
27029         var ti = new Roo.Toolbar.Item(td.firstChild);
27030         ti.render(td);
27031         this.items.add(ti);
27032         return ti;
27033     },
27034
27035     /**
27036      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27037      * @type Roo.util.MixedCollection  
27038      */
27039     fields : false,
27040     
27041     /**
27042      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27043      * Note: the field should not have been rendered yet. For a field that has already been
27044      * rendered, use {@link #addElement}.
27045      * @param {Roo.form.Field} field
27046      * @return {Roo.ToolbarItem}
27047      */
27048      
27049       
27050     addField : function(field) {
27051         if (!this.fields) {
27052             var autoId = 0;
27053             this.fields = new Roo.util.MixedCollection(false, function(o){
27054                 return o.id || ("item" + (++autoId));
27055             });
27056
27057         }
27058         
27059         var td = this.nextBlock();
27060         field.render(td);
27061         var ti = new Roo.Toolbar.Item(td.firstChild);
27062         ti.render(td);
27063         this.items.add(ti);
27064         this.fields.add(field);
27065         return ti;
27066     },
27067     /**
27068      * Hide the toolbar
27069      * @method hide
27070      */
27071      
27072       
27073     hide : function()
27074     {
27075         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27076         this.el.child('div').hide();
27077     },
27078     /**
27079      * Show the toolbar
27080      * @method show
27081      */
27082     show : function()
27083     {
27084         this.el.child('div').show();
27085     },
27086       
27087     // private
27088     nextBlock : function(){
27089         var td = document.createElement("td");
27090         this.tr.appendChild(td);
27091         return td;
27092     },
27093
27094     // private
27095     destroy : function(){
27096         if(this.items){ // rendered?
27097             Roo.destroy.apply(Roo, this.items.items);
27098         }
27099         if(this.fields){ // rendered?
27100             Roo.destroy.apply(Roo, this.fields.items);
27101         }
27102         Roo.Element.uncache(this.el, this.tr);
27103     }
27104 };
27105
27106 /**
27107  * @class Roo.Toolbar.Item
27108  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27109  * @constructor
27110  * Creates a new Item
27111  * @param {HTMLElement} el 
27112  */
27113 Roo.Toolbar.Item = function(el){
27114     this.el = Roo.getDom(el);
27115     this.id = Roo.id(this.el);
27116     this.hidden = false;
27117 };
27118
27119 Roo.Toolbar.Item.prototype = {
27120     
27121     /**
27122      * Get this item's HTML Element
27123      * @return {HTMLElement}
27124      */
27125     getEl : function(){
27126        return this.el;  
27127     },
27128
27129     // private
27130     render : function(td){
27131         this.td = td;
27132         td.appendChild(this.el);
27133     },
27134     
27135     /**
27136      * Removes and destroys this item.
27137      */
27138     destroy : function(){
27139         this.td.parentNode.removeChild(this.td);
27140     },
27141     
27142     /**
27143      * Shows this item.
27144      */
27145     show: function(){
27146         this.hidden = false;
27147         this.td.style.display = "";
27148     },
27149     
27150     /**
27151      * Hides this item.
27152      */
27153     hide: function(){
27154         this.hidden = true;
27155         this.td.style.display = "none";
27156     },
27157     
27158     /**
27159      * Convenience function for boolean show/hide.
27160      * @param {Boolean} visible true to show/false to hide
27161      */
27162     setVisible: function(visible){
27163         if(visible) {
27164             this.show();
27165         }else{
27166             this.hide();
27167         }
27168     },
27169     
27170     /**
27171      * Try to focus this item.
27172      */
27173     focus : function(){
27174         Roo.fly(this.el).focus();
27175     },
27176     
27177     /**
27178      * Disables this item.
27179      */
27180     disable : function(){
27181         Roo.fly(this.td).addClass("x-item-disabled");
27182         this.disabled = true;
27183         this.el.disabled = true;
27184     },
27185     
27186     /**
27187      * Enables this item.
27188      */
27189     enable : function(){
27190         Roo.fly(this.td).removeClass("x-item-disabled");
27191         this.disabled = false;
27192         this.el.disabled = false;
27193     }
27194 };
27195
27196
27197 /**
27198  * @class Roo.Toolbar.Separator
27199  * @extends Roo.Toolbar.Item
27200  * A simple toolbar separator class
27201  * @constructor
27202  * Creates a new Separator
27203  */
27204 Roo.Toolbar.Separator = function(){
27205     var s = document.createElement("span");
27206     s.className = "ytb-sep";
27207     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27208 };
27209 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27210     enable:Roo.emptyFn,
27211     disable:Roo.emptyFn,
27212     focus:Roo.emptyFn
27213 });
27214
27215 /**
27216  * @class Roo.Toolbar.Spacer
27217  * @extends Roo.Toolbar.Item
27218  * A simple element that adds extra horizontal space to a toolbar.
27219  * @constructor
27220  * Creates a new Spacer
27221  */
27222 Roo.Toolbar.Spacer = function(){
27223     var s = document.createElement("div");
27224     s.className = "ytb-spacer";
27225     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27226 };
27227 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27228     enable:Roo.emptyFn,
27229     disable:Roo.emptyFn,
27230     focus:Roo.emptyFn
27231 });
27232
27233 /**
27234  * @class Roo.Toolbar.Fill
27235  * @extends Roo.Toolbar.Spacer
27236  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27237  * @constructor
27238  * Creates a new Spacer
27239  */
27240 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27241     // private
27242     render : function(td){
27243         td.style.width = '100%';
27244         Roo.Toolbar.Fill.superclass.render.call(this, td);
27245     }
27246 });
27247
27248 /**
27249  * @class Roo.Toolbar.TextItem
27250  * @extends Roo.Toolbar.Item
27251  * A simple class that renders text directly into a toolbar.
27252  * @constructor
27253  * Creates a new TextItem
27254  * @param {String} text
27255  */
27256 Roo.Toolbar.TextItem = function(text){
27257     if (typeof(text) == 'object') {
27258         text = text.text;
27259     }
27260     var s = document.createElement("span");
27261     s.className = "ytb-text";
27262     s.innerHTML = text;
27263     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27264 };
27265 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27266     enable:Roo.emptyFn,
27267     disable:Roo.emptyFn,
27268     focus:Roo.emptyFn
27269 });
27270
27271 /**
27272  * @class Roo.Toolbar.Button
27273  * @extends Roo.Button
27274  * A button that renders into a toolbar.
27275  * @constructor
27276  * Creates a new Button
27277  * @param {Object} config A standard {@link Roo.Button} config object
27278  */
27279 Roo.Toolbar.Button = function(config){
27280     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27281 };
27282 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27283     render : function(td){
27284         this.td = td;
27285         Roo.Toolbar.Button.superclass.render.call(this, td);
27286     },
27287     
27288     /**
27289      * Removes and destroys this button
27290      */
27291     destroy : function(){
27292         Roo.Toolbar.Button.superclass.destroy.call(this);
27293         this.td.parentNode.removeChild(this.td);
27294     },
27295     
27296     /**
27297      * Shows this button
27298      */
27299     show: function(){
27300         this.hidden = false;
27301         this.td.style.display = "";
27302     },
27303     
27304     /**
27305      * Hides this button
27306      */
27307     hide: function(){
27308         this.hidden = true;
27309         this.td.style.display = "none";
27310     },
27311
27312     /**
27313      * Disables this item
27314      */
27315     disable : function(){
27316         Roo.fly(this.td).addClass("x-item-disabled");
27317         this.disabled = true;
27318     },
27319
27320     /**
27321      * Enables this item
27322      */
27323     enable : function(){
27324         Roo.fly(this.td).removeClass("x-item-disabled");
27325         this.disabled = false;
27326     }
27327 });
27328 // backwards compat
27329 Roo.ToolbarButton = Roo.Toolbar.Button;
27330
27331 /**
27332  * @class Roo.Toolbar.SplitButton
27333  * @extends Roo.SplitButton
27334  * A menu button that renders into a toolbar.
27335  * @constructor
27336  * Creates a new SplitButton
27337  * @param {Object} config A standard {@link Roo.SplitButton} config object
27338  */
27339 Roo.Toolbar.SplitButton = function(config){
27340     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27341 };
27342 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27343     render : function(td){
27344         this.td = td;
27345         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27346     },
27347     
27348     /**
27349      * Removes and destroys this button
27350      */
27351     destroy : function(){
27352         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27353         this.td.parentNode.removeChild(this.td);
27354     },
27355     
27356     /**
27357      * Shows this button
27358      */
27359     show: function(){
27360         this.hidden = false;
27361         this.td.style.display = "";
27362     },
27363     
27364     /**
27365      * Hides this button
27366      */
27367     hide: function(){
27368         this.hidden = true;
27369         this.td.style.display = "none";
27370     }
27371 });
27372
27373 // backwards compat
27374 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27375  * Based on:
27376  * Ext JS Library 1.1.1
27377  * Copyright(c) 2006-2007, Ext JS, LLC.
27378  *
27379  * Originally Released Under LGPL - original licence link has changed is not relivant.
27380  *
27381  * Fork - LGPL
27382  * <script type="text/javascript">
27383  */
27384  
27385 /**
27386  * @class Roo.PagingToolbar
27387  * @extends Roo.Toolbar
27388  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27389  * @constructor
27390  * Create a new PagingToolbar
27391  * @param {Object} config The config object
27392  */
27393 Roo.PagingToolbar = function(el, ds, config)
27394 {
27395     // old args format still supported... - xtype is prefered..
27396     if (typeof(el) == 'object' && el.xtype) {
27397         // created from xtype...
27398         config = el;
27399         ds = el.dataSource;
27400         el = config.container;
27401     }
27402     var items = [];
27403     if (config.items) {
27404         items = config.items;
27405         config.items = [];
27406     }
27407     
27408     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27409     this.ds = ds;
27410     this.cursor = 0;
27411     this.renderButtons(this.el);
27412     this.bind(ds);
27413     
27414     // supprot items array.
27415    
27416     Roo.each(items, function(e) {
27417         this.add(Roo.factory(e));
27418     },this);
27419     
27420 };
27421
27422 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27423     /**
27424      * @cfg {Roo.data.Store} dataSource
27425      * The underlying data store providing the paged data
27426      */
27427     /**
27428      * @cfg {String/HTMLElement/Element} container
27429      * container The id or element that will contain the toolbar
27430      */
27431     /**
27432      * @cfg {Boolean} displayInfo
27433      * True to display the displayMsg (defaults to false)
27434      */
27435     /**
27436      * @cfg {Number} pageSize
27437      * The number of records to display per page (defaults to 20)
27438      */
27439     pageSize: 20,
27440     /**
27441      * @cfg {String} displayMsg
27442      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27443      */
27444     displayMsg : 'Displaying {0} - {1} of {2}',
27445     /**
27446      * @cfg {String} emptyMsg
27447      * The message to display when no records are found (defaults to "No data to display")
27448      */
27449     emptyMsg : 'No data to display',
27450     /**
27451      * Customizable piece of the default paging text (defaults to "Page")
27452      * @type String
27453      */
27454     beforePageText : "Page",
27455     /**
27456      * Customizable piece of the default paging text (defaults to "of %0")
27457      * @type String
27458      */
27459     afterPageText : "of {0}",
27460     /**
27461      * Customizable piece of the default paging text (defaults to "First Page")
27462      * @type String
27463      */
27464     firstText : "First Page",
27465     /**
27466      * Customizable piece of the default paging text (defaults to "Previous Page")
27467      * @type String
27468      */
27469     prevText : "Previous Page",
27470     /**
27471      * Customizable piece of the default paging text (defaults to "Next Page")
27472      * @type String
27473      */
27474     nextText : "Next Page",
27475     /**
27476      * Customizable piece of the default paging text (defaults to "Last Page")
27477      * @type String
27478      */
27479     lastText : "Last Page",
27480     /**
27481      * Customizable piece of the default paging text (defaults to "Refresh")
27482      * @type String
27483      */
27484     refreshText : "Refresh",
27485
27486     // private
27487     renderButtons : function(el){
27488         Roo.PagingToolbar.superclass.render.call(this, el);
27489         this.first = this.addButton({
27490             tooltip: this.firstText,
27491             cls: "x-btn-icon x-grid-page-first",
27492             disabled: true,
27493             handler: this.onClick.createDelegate(this, ["first"])
27494         });
27495         this.prev = this.addButton({
27496             tooltip: this.prevText,
27497             cls: "x-btn-icon x-grid-page-prev",
27498             disabled: true,
27499             handler: this.onClick.createDelegate(this, ["prev"])
27500         });
27501         //this.addSeparator();
27502         this.add(this.beforePageText);
27503         this.field = Roo.get(this.addDom({
27504            tag: "input",
27505            type: "text",
27506            size: "3",
27507            value: "1",
27508            cls: "x-grid-page-number"
27509         }).el);
27510         this.field.on("keydown", this.onPagingKeydown, this);
27511         this.field.on("focus", function(){this.dom.select();});
27512         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27513         this.field.setHeight(18);
27514         //this.addSeparator();
27515         this.next = this.addButton({
27516             tooltip: this.nextText,
27517             cls: "x-btn-icon x-grid-page-next",
27518             disabled: true,
27519             handler: this.onClick.createDelegate(this, ["next"])
27520         });
27521         this.last = this.addButton({
27522             tooltip: this.lastText,
27523             cls: "x-btn-icon x-grid-page-last",
27524             disabled: true,
27525             handler: this.onClick.createDelegate(this, ["last"])
27526         });
27527         //this.addSeparator();
27528         this.loading = this.addButton({
27529             tooltip: this.refreshText,
27530             cls: "x-btn-icon x-grid-loading",
27531             handler: this.onClick.createDelegate(this, ["refresh"])
27532         });
27533
27534         if(this.displayInfo){
27535             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27536         }
27537     },
27538
27539     // private
27540     updateInfo : function(){
27541         if(this.displayEl){
27542             var count = this.ds.getCount();
27543             var msg = count == 0 ?
27544                 this.emptyMsg :
27545                 String.format(
27546                     this.displayMsg,
27547                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27548                 );
27549             this.displayEl.update(msg);
27550         }
27551     },
27552
27553     // private
27554     onLoad : function(ds, r, o){
27555        this.cursor = o.params ? o.params.start : 0;
27556        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27557
27558        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27559        this.field.dom.value = ap;
27560        this.first.setDisabled(ap == 1);
27561        this.prev.setDisabled(ap == 1);
27562        this.next.setDisabled(ap == ps);
27563        this.last.setDisabled(ap == ps);
27564        this.loading.enable();
27565        this.updateInfo();
27566     },
27567
27568     // private
27569     getPageData : function(){
27570         var total = this.ds.getTotalCount();
27571         return {
27572             total : total,
27573             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27574             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27575         };
27576     },
27577
27578     // private
27579     onLoadError : function(){
27580         this.loading.enable();
27581     },
27582
27583     // private
27584     onPagingKeydown : function(e){
27585         var k = e.getKey();
27586         var d = this.getPageData();
27587         if(k == e.RETURN){
27588             var v = this.field.dom.value, pageNum;
27589             if(!v || isNaN(pageNum = parseInt(v, 10))){
27590                 this.field.dom.value = d.activePage;
27591                 return;
27592             }
27593             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27594             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27595             e.stopEvent();
27596         }
27597         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))
27598         {
27599           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27600           this.field.dom.value = pageNum;
27601           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27602           e.stopEvent();
27603         }
27604         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27605         {
27606           var v = this.field.dom.value, pageNum; 
27607           var increment = (e.shiftKey) ? 10 : 1;
27608           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27609             increment *= -1;
27610           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27611             this.field.dom.value = d.activePage;
27612             return;
27613           }
27614           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27615           {
27616             this.field.dom.value = parseInt(v, 10) + increment;
27617             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27618             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27619           }
27620           e.stopEvent();
27621         }
27622     },
27623
27624     // private
27625     beforeLoad : function(){
27626         if(this.loading){
27627             this.loading.disable();
27628         }
27629     },
27630
27631     // private
27632     onClick : function(which){
27633         var ds = this.ds;
27634         switch(which){
27635             case "first":
27636                 ds.load({params:{start: 0, limit: this.pageSize}});
27637             break;
27638             case "prev":
27639                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27640             break;
27641             case "next":
27642                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27643             break;
27644             case "last":
27645                 var total = ds.getTotalCount();
27646                 var extra = total % this.pageSize;
27647                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27648                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27649             break;
27650             case "refresh":
27651                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27652             break;
27653         }
27654     },
27655
27656     /**
27657      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27658      * @param {Roo.data.Store} store The data store to unbind
27659      */
27660     unbind : function(ds){
27661         ds.un("beforeload", this.beforeLoad, this);
27662         ds.un("load", this.onLoad, this);
27663         ds.un("loadexception", this.onLoadError, this);
27664         ds.un("remove", this.updateInfo, this);
27665         ds.un("add", this.updateInfo, this);
27666         this.ds = undefined;
27667     },
27668
27669     /**
27670      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27671      * @param {Roo.data.Store} store The data store to bind
27672      */
27673     bind : function(ds){
27674         ds.on("beforeload", this.beforeLoad, this);
27675         ds.on("load", this.onLoad, this);
27676         ds.on("loadexception", this.onLoadError, this);
27677         ds.on("remove", this.updateInfo, this);
27678         ds.on("add", this.updateInfo, this);
27679         this.ds = ds;
27680     }
27681 });/*
27682  * Based on:
27683  * Ext JS Library 1.1.1
27684  * Copyright(c) 2006-2007, Ext JS, LLC.
27685  *
27686  * Originally Released Under LGPL - original licence link has changed is not relivant.
27687  *
27688  * Fork - LGPL
27689  * <script type="text/javascript">
27690  */
27691
27692 /**
27693  * @class Roo.Resizable
27694  * @extends Roo.util.Observable
27695  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27696  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27697  * 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
27698  * the element will be wrapped for you automatically.</p>
27699  * <p>Here is the list of valid resize handles:</p>
27700  * <pre>
27701 Value   Description
27702 ------  -------------------
27703  'n'     north
27704  's'     south
27705  'e'     east
27706  'w'     west
27707  'nw'    northwest
27708  'sw'    southwest
27709  'se'    southeast
27710  'ne'    northeast
27711  'hd'    horizontal drag
27712  'all'   all
27713 </pre>
27714  * <p>Here's an example showing the creation of a typical Resizable:</p>
27715  * <pre><code>
27716 var resizer = new Roo.Resizable("element-id", {
27717     handles: 'all',
27718     minWidth: 200,
27719     minHeight: 100,
27720     maxWidth: 500,
27721     maxHeight: 400,
27722     pinned: true
27723 });
27724 resizer.on("resize", myHandler);
27725 </code></pre>
27726  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27727  * resizer.east.setDisplayed(false);</p>
27728  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27729  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27730  * resize operation's new size (defaults to [0, 0])
27731  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27732  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27733  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27734  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27735  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27736  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27737  * @cfg {Number} width The width of the element in pixels (defaults to null)
27738  * @cfg {Number} height The height of the element in pixels (defaults to null)
27739  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27740  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27741  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27742  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27743  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27744  * in favor of the handles config option (defaults to false)
27745  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27746  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27747  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27748  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27749  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27750  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27751  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27752  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27753  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27754  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27755  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27756  * @constructor
27757  * Create a new resizable component
27758  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27759  * @param {Object} config configuration options
27760   */
27761 Roo.Resizable = function(el, config)
27762 {
27763     this.el = Roo.get(el);
27764
27765     if(config && config.wrap){
27766         config.resizeChild = this.el;
27767         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27768         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27769         this.el.setStyle("overflow", "hidden");
27770         this.el.setPositioning(config.resizeChild.getPositioning());
27771         config.resizeChild.clearPositioning();
27772         if(!config.width || !config.height){
27773             var csize = config.resizeChild.getSize();
27774             this.el.setSize(csize.width, csize.height);
27775         }
27776         if(config.pinned && !config.adjustments){
27777             config.adjustments = "auto";
27778         }
27779     }
27780
27781     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27782     this.proxy.unselectable();
27783     this.proxy.enableDisplayMode('block');
27784
27785     Roo.apply(this, config);
27786
27787     if(this.pinned){
27788         this.disableTrackOver = true;
27789         this.el.addClass("x-resizable-pinned");
27790     }
27791     // if the element isn't positioned, make it relative
27792     var position = this.el.getStyle("position");
27793     if(position != "absolute" && position != "fixed"){
27794         this.el.setStyle("position", "relative");
27795     }
27796     if(!this.handles){ // no handles passed, must be legacy style
27797         this.handles = 's,e,se';
27798         if(this.multiDirectional){
27799             this.handles += ',n,w';
27800         }
27801     }
27802     if(this.handles == "all"){
27803         this.handles = "n s e w ne nw se sw";
27804     }
27805     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27806     var ps = Roo.Resizable.positions;
27807     for(var i = 0, len = hs.length; i < len; i++){
27808         if(hs[i] && ps[hs[i]]){
27809             var pos = ps[hs[i]];
27810             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27811         }
27812     }
27813     // legacy
27814     this.corner = this.southeast;
27815     
27816     // updateBox = the box can move..
27817     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27818         this.updateBox = true;
27819     }
27820
27821     this.activeHandle = null;
27822
27823     if(this.resizeChild){
27824         if(typeof this.resizeChild == "boolean"){
27825             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27826         }else{
27827             this.resizeChild = Roo.get(this.resizeChild, true);
27828         }
27829     }
27830     
27831     if(this.adjustments == "auto"){
27832         var rc = this.resizeChild;
27833         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27834         if(rc && (hw || hn)){
27835             rc.position("relative");
27836             rc.setLeft(hw ? hw.el.getWidth() : 0);
27837             rc.setTop(hn ? hn.el.getHeight() : 0);
27838         }
27839         this.adjustments = [
27840             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27841             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27842         ];
27843     }
27844
27845     if(this.draggable){
27846         this.dd = this.dynamic ?
27847             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27848         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27849     }
27850
27851     // public events
27852     this.addEvents({
27853         /**
27854          * @event beforeresize
27855          * Fired before resize is allowed. Set enabled to false to cancel resize.
27856          * @param {Roo.Resizable} this
27857          * @param {Roo.EventObject} e The mousedown event
27858          */
27859         "beforeresize" : true,
27860         /**
27861          * @event resize
27862          * Fired after a resize.
27863          * @param {Roo.Resizable} this
27864          * @param {Number} width The new width
27865          * @param {Number} height The new height
27866          * @param {Roo.EventObject} e The mouseup event
27867          */
27868         "resize" : true
27869     });
27870
27871     if(this.width !== null && this.height !== null){
27872         this.resizeTo(this.width, this.height);
27873     }else{
27874         this.updateChildSize();
27875     }
27876     if(Roo.isIE){
27877         this.el.dom.style.zoom = 1;
27878     }
27879     Roo.Resizable.superclass.constructor.call(this);
27880 };
27881
27882 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27883         resizeChild : false,
27884         adjustments : [0, 0],
27885         minWidth : 5,
27886         minHeight : 5,
27887         maxWidth : 10000,
27888         maxHeight : 10000,
27889         enabled : true,
27890         animate : false,
27891         duration : .35,
27892         dynamic : false,
27893         handles : false,
27894         multiDirectional : false,
27895         disableTrackOver : false,
27896         easing : 'easeOutStrong',
27897         widthIncrement : 0,
27898         heightIncrement : 0,
27899         pinned : false,
27900         width : null,
27901         height : null,
27902         preserveRatio : false,
27903         transparent: false,
27904         minX: 0,
27905         minY: 0,
27906         draggable: false,
27907
27908         /**
27909          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27910          */
27911         constrainTo: undefined,
27912         /**
27913          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27914          */
27915         resizeRegion: undefined,
27916
27917
27918     /**
27919      * Perform a manual resize
27920      * @param {Number} width
27921      * @param {Number} height
27922      */
27923     resizeTo : function(width, height){
27924         this.el.setSize(width, height);
27925         this.updateChildSize();
27926         this.fireEvent("resize", this, width, height, null);
27927     },
27928
27929     // private
27930     startSizing : function(e, handle){
27931         this.fireEvent("beforeresize", this, e);
27932         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27933
27934             if(!this.overlay){
27935                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27936                 this.overlay.unselectable();
27937                 this.overlay.enableDisplayMode("block");
27938                 this.overlay.on("mousemove", this.onMouseMove, this);
27939                 this.overlay.on("mouseup", this.onMouseUp, this);
27940             }
27941             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27942
27943             this.resizing = true;
27944             this.startBox = this.el.getBox();
27945             this.startPoint = e.getXY();
27946             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27947                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27948
27949             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27950             this.overlay.show();
27951
27952             if(this.constrainTo) {
27953                 var ct = Roo.get(this.constrainTo);
27954                 this.resizeRegion = ct.getRegion().adjust(
27955                     ct.getFrameWidth('t'),
27956                     ct.getFrameWidth('l'),
27957                     -ct.getFrameWidth('b'),
27958                     -ct.getFrameWidth('r')
27959                 );
27960             }
27961
27962             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27963             this.proxy.show();
27964             this.proxy.setBox(this.startBox);
27965             if(!this.dynamic){
27966                 this.proxy.setStyle('visibility', 'visible');
27967             }
27968         }
27969     },
27970
27971     // private
27972     onMouseDown : function(handle, e){
27973         if(this.enabled){
27974             e.stopEvent();
27975             this.activeHandle = handle;
27976             this.startSizing(e, handle);
27977         }
27978     },
27979
27980     // private
27981     onMouseUp : function(e){
27982         var size = this.resizeElement();
27983         this.resizing = false;
27984         this.handleOut();
27985         this.overlay.hide();
27986         this.proxy.hide();
27987         this.fireEvent("resize", this, size.width, size.height, e);
27988     },
27989
27990     // private
27991     updateChildSize : function(){
27992         if(this.resizeChild){
27993             var el = this.el;
27994             var child = this.resizeChild;
27995             var adj = this.adjustments;
27996             if(el.dom.offsetWidth){
27997                 var b = el.getSize(true);
27998                 child.setSize(b.width+adj[0], b.height+adj[1]);
27999             }
28000             // Second call here for IE
28001             // The first call enables instant resizing and
28002             // the second call corrects scroll bars if they
28003             // exist
28004             if(Roo.isIE){
28005                 setTimeout(function(){
28006                     if(el.dom.offsetWidth){
28007                         var b = el.getSize(true);
28008                         child.setSize(b.width+adj[0], b.height+adj[1]);
28009                     }
28010                 }, 10);
28011             }
28012         }
28013     },
28014
28015     // private
28016     snap : function(value, inc, min){
28017         if(!inc || !value) return value;
28018         var newValue = value;
28019         var m = value % inc;
28020         if(m > 0){
28021             if(m > (inc/2)){
28022                 newValue = value + (inc-m);
28023             }else{
28024                 newValue = value - m;
28025             }
28026         }
28027         return Math.max(min, newValue);
28028     },
28029
28030     // private
28031     resizeElement : function(){
28032         var box = this.proxy.getBox();
28033         if(this.updateBox){
28034             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28035         }else{
28036             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28037         }
28038         this.updateChildSize();
28039         if(!this.dynamic){
28040             this.proxy.hide();
28041         }
28042         return box;
28043     },
28044
28045     // private
28046     constrain : function(v, diff, m, mx){
28047         if(v - diff < m){
28048             diff = v - m;
28049         }else if(v - diff > mx){
28050             diff = mx - v;
28051         }
28052         return diff;
28053     },
28054
28055     // private
28056     onMouseMove : function(e){
28057         if(this.enabled){
28058             try{// try catch so if something goes wrong the user doesn't get hung
28059
28060             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28061                 return;
28062             }
28063
28064             //var curXY = this.startPoint;
28065             var curSize = this.curSize || this.startBox;
28066             var x = this.startBox.x, y = this.startBox.y;
28067             var ox = x, oy = y;
28068             var w = curSize.width, h = curSize.height;
28069             var ow = w, oh = h;
28070             var mw = this.minWidth, mh = this.minHeight;
28071             var mxw = this.maxWidth, mxh = this.maxHeight;
28072             var wi = this.widthIncrement;
28073             var hi = this.heightIncrement;
28074
28075             var eventXY = e.getXY();
28076             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28077             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28078
28079             var pos = this.activeHandle.position;
28080
28081             switch(pos){
28082                 case "east":
28083                     w += diffX;
28084                     w = Math.min(Math.max(mw, w), mxw);
28085                     break;
28086              
28087                 case "south":
28088                     h += diffY;
28089                     h = Math.min(Math.max(mh, h), mxh);
28090                     break;
28091                 case "southeast":
28092                     w += diffX;
28093                     h += diffY;
28094                     w = Math.min(Math.max(mw, w), mxw);
28095                     h = Math.min(Math.max(mh, h), mxh);
28096                     break;
28097                 case "north":
28098                     diffY = this.constrain(h, diffY, mh, mxh);
28099                     y += diffY;
28100                     h -= diffY;
28101                     break;
28102                 case "hdrag":
28103                     
28104                     if (wi) {
28105                         var adiffX = Math.abs(diffX);
28106                         var sub = (adiffX % wi); // how much 
28107                         if (sub > (wi/2)) { // far enough to snap
28108                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28109                         } else {
28110                             // remove difference.. 
28111                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28112                         }
28113                     }
28114                     x += diffX;
28115                     x = Math.max(this.minX, x);
28116                     break;
28117                 case "west":
28118                     diffX = this.constrain(w, diffX, mw, mxw);
28119                     x += diffX;
28120                     w -= diffX;
28121                     break;
28122                 case "northeast":
28123                     w += diffX;
28124                     w = Math.min(Math.max(mw, w), mxw);
28125                     diffY = this.constrain(h, diffY, mh, mxh);
28126                     y += diffY;
28127                     h -= diffY;
28128                     break;
28129                 case "northwest":
28130                     diffX = this.constrain(w, diffX, mw, mxw);
28131                     diffY = this.constrain(h, diffY, mh, mxh);
28132                     y += diffY;
28133                     h -= diffY;
28134                     x += diffX;
28135                     w -= diffX;
28136                     break;
28137                case "southwest":
28138                     diffX = this.constrain(w, diffX, mw, mxw);
28139                     h += diffY;
28140                     h = Math.min(Math.max(mh, h), mxh);
28141                     x += diffX;
28142                     w -= diffX;
28143                     break;
28144             }
28145
28146             var sw = this.snap(w, wi, mw);
28147             var sh = this.snap(h, hi, mh);
28148             if(sw != w || sh != h){
28149                 switch(pos){
28150                     case "northeast":
28151                         y -= sh - h;
28152                     break;
28153                     case "north":
28154                         y -= sh - h;
28155                         break;
28156                     case "southwest":
28157                         x -= sw - w;
28158                     break;
28159                     case "west":
28160                         x -= sw - w;
28161                         break;
28162                     case "northwest":
28163                         x -= sw - w;
28164                         y -= sh - h;
28165                     break;
28166                 }
28167                 w = sw;
28168                 h = sh;
28169             }
28170
28171             if(this.preserveRatio){
28172                 switch(pos){
28173                     case "southeast":
28174                     case "east":
28175                         h = oh * (w/ow);
28176                         h = Math.min(Math.max(mh, h), mxh);
28177                         w = ow * (h/oh);
28178                        break;
28179                     case "south":
28180                         w = ow * (h/oh);
28181                         w = Math.min(Math.max(mw, w), mxw);
28182                         h = oh * (w/ow);
28183                         break;
28184                     case "northeast":
28185                         w = ow * (h/oh);
28186                         w = Math.min(Math.max(mw, w), mxw);
28187                         h = oh * (w/ow);
28188                     break;
28189                     case "north":
28190                         var tw = w;
28191                         w = ow * (h/oh);
28192                         w = Math.min(Math.max(mw, w), mxw);
28193                         h = oh * (w/ow);
28194                         x += (tw - w) / 2;
28195                         break;
28196                     case "southwest":
28197                         h = oh * (w/ow);
28198                         h = Math.min(Math.max(mh, h), mxh);
28199                         var tw = w;
28200                         w = ow * (h/oh);
28201                         x += tw - w;
28202                         break;
28203                     case "west":
28204                         var th = h;
28205                         h = oh * (w/ow);
28206                         h = Math.min(Math.max(mh, h), mxh);
28207                         y += (th - h) / 2;
28208                         var tw = w;
28209                         w = ow * (h/oh);
28210                         x += tw - w;
28211                        break;
28212                     case "northwest":
28213                         var tw = w;
28214                         var th = h;
28215                         h = oh * (w/ow);
28216                         h = Math.min(Math.max(mh, h), mxh);
28217                         w = ow * (h/oh);
28218                         y += th - h;
28219                         x += tw - w;
28220                        break;
28221
28222                 }
28223             }
28224             if (pos == 'hdrag') {
28225                 w = ow;
28226             }
28227             this.proxy.setBounds(x, y, w, h);
28228             if(this.dynamic){
28229                 this.resizeElement();
28230             }
28231             }catch(e){}
28232         }
28233     },
28234
28235     // private
28236     handleOver : function(){
28237         if(this.enabled){
28238             this.el.addClass("x-resizable-over");
28239         }
28240     },
28241
28242     // private
28243     handleOut : function(){
28244         if(!this.resizing){
28245             this.el.removeClass("x-resizable-over");
28246         }
28247     },
28248
28249     /**
28250      * Returns the element this component is bound to.
28251      * @return {Roo.Element}
28252      */
28253     getEl : function(){
28254         return this.el;
28255     },
28256
28257     /**
28258      * Returns the resizeChild element (or null).
28259      * @return {Roo.Element}
28260      */
28261     getResizeChild : function(){
28262         return this.resizeChild;
28263     },
28264
28265     /**
28266      * Destroys this resizable. If the element was wrapped and
28267      * removeEl is not true then the element remains.
28268      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28269      */
28270     destroy : function(removeEl){
28271         this.proxy.remove();
28272         if(this.overlay){
28273             this.overlay.removeAllListeners();
28274             this.overlay.remove();
28275         }
28276         var ps = Roo.Resizable.positions;
28277         for(var k in ps){
28278             if(typeof ps[k] != "function" && this[ps[k]]){
28279                 var h = this[ps[k]];
28280                 h.el.removeAllListeners();
28281                 h.el.remove();
28282             }
28283         }
28284         if(removeEl){
28285             this.el.update("");
28286             this.el.remove();
28287         }
28288     }
28289 });
28290
28291 // private
28292 // hash to map config positions to true positions
28293 Roo.Resizable.positions = {
28294     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28295     hd: "hdrag"
28296 };
28297
28298 // private
28299 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28300     if(!this.tpl){
28301         // only initialize the template if resizable is used
28302         var tpl = Roo.DomHelper.createTemplate(
28303             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28304         );
28305         tpl.compile();
28306         Roo.Resizable.Handle.prototype.tpl = tpl;
28307     }
28308     this.position = pos;
28309     this.rz = rz;
28310     // show north drag fro topdra
28311     var handlepos = pos == 'hdrag' ? 'north' : pos;
28312     
28313     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28314     if (pos == 'hdrag') {
28315         this.el.setStyle('cursor', 'pointer');
28316     }
28317     this.el.unselectable();
28318     if(transparent){
28319         this.el.setOpacity(0);
28320     }
28321     this.el.on("mousedown", this.onMouseDown, this);
28322     if(!disableTrackOver){
28323         this.el.on("mouseover", this.onMouseOver, this);
28324         this.el.on("mouseout", this.onMouseOut, this);
28325     }
28326 };
28327
28328 // private
28329 Roo.Resizable.Handle.prototype = {
28330     afterResize : function(rz){
28331         // do nothing
28332     },
28333     // private
28334     onMouseDown : function(e){
28335         this.rz.onMouseDown(this, e);
28336     },
28337     // private
28338     onMouseOver : function(e){
28339         this.rz.handleOver(this, e);
28340     },
28341     // private
28342     onMouseOut : function(e){
28343         this.rz.handleOut(this, e);
28344     }
28345 };/*
28346  * Based on:
28347  * Ext JS Library 1.1.1
28348  * Copyright(c) 2006-2007, Ext JS, LLC.
28349  *
28350  * Originally Released Under LGPL - original licence link has changed is not relivant.
28351  *
28352  * Fork - LGPL
28353  * <script type="text/javascript">
28354  */
28355
28356 /**
28357  * @class Roo.Editor
28358  * @extends Roo.Component
28359  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28360  * @constructor
28361  * Create a new Editor
28362  * @param {Roo.form.Field} field The Field object (or descendant)
28363  * @param {Object} config The config object
28364  */
28365 Roo.Editor = function(field, config){
28366     Roo.Editor.superclass.constructor.call(this, config);
28367     this.field = field;
28368     this.addEvents({
28369         /**
28370              * @event beforestartedit
28371              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28372              * false from the handler of this event.
28373              * @param {Editor} this
28374              * @param {Roo.Element} boundEl The underlying element bound to this editor
28375              * @param {Mixed} value The field value being set
28376              */
28377         "beforestartedit" : true,
28378         /**
28379              * @event startedit
28380              * Fires when this editor is displayed
28381              * @param {Roo.Element} boundEl The underlying element bound to this editor
28382              * @param {Mixed} value The starting field value
28383              */
28384         "startedit" : true,
28385         /**
28386              * @event beforecomplete
28387              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28388              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28389              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28390              * event will not fire since no edit actually occurred.
28391              * @param {Editor} this
28392              * @param {Mixed} value The current field value
28393              * @param {Mixed} startValue The original field value
28394              */
28395         "beforecomplete" : true,
28396         /**
28397              * @event complete
28398              * Fires after editing is complete and any changed value has been written to the underlying field.
28399              * @param {Editor} this
28400              * @param {Mixed} value The current field value
28401              * @param {Mixed} startValue The original field value
28402              */
28403         "complete" : true,
28404         /**
28405          * @event specialkey
28406          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28407          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28408          * @param {Roo.form.Field} this
28409          * @param {Roo.EventObject} e The event object
28410          */
28411         "specialkey" : true
28412     });
28413 };
28414
28415 Roo.extend(Roo.Editor, Roo.Component, {
28416     /**
28417      * @cfg {Boolean/String} autosize
28418      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28419      * or "height" to adopt the height only (defaults to false)
28420      */
28421     /**
28422      * @cfg {Boolean} revertInvalid
28423      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28424      * validation fails (defaults to true)
28425      */
28426     /**
28427      * @cfg {Boolean} ignoreNoChange
28428      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28429      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28430      * will never be ignored.
28431      */
28432     /**
28433      * @cfg {Boolean} hideEl
28434      * False to keep the bound element visible while the editor is displayed (defaults to true)
28435      */
28436     /**
28437      * @cfg {Mixed} value
28438      * The data value of the underlying field (defaults to "")
28439      */
28440     value : "",
28441     /**
28442      * @cfg {String} alignment
28443      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28444      */
28445     alignment: "c-c?",
28446     /**
28447      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28448      * for bottom-right shadow (defaults to "frame")
28449      */
28450     shadow : "frame",
28451     /**
28452      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28453      */
28454     constrain : false,
28455     /**
28456      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28457      */
28458     completeOnEnter : false,
28459     /**
28460      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28461      */
28462     cancelOnEsc : false,
28463     /**
28464      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28465      */
28466     updateEl : false,
28467
28468     // private
28469     onRender : function(ct, position){
28470         this.el = new Roo.Layer({
28471             shadow: this.shadow,
28472             cls: "x-editor",
28473             parentEl : ct,
28474             shim : this.shim,
28475             shadowOffset:4,
28476             id: this.id,
28477             constrain: this.constrain
28478         });
28479         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28480         if(this.field.msgTarget != 'title'){
28481             this.field.msgTarget = 'qtip';
28482         }
28483         this.field.render(this.el);
28484         if(Roo.isGecko){
28485             this.field.el.dom.setAttribute('autocomplete', 'off');
28486         }
28487         this.field.on("specialkey", this.onSpecialKey, this);
28488         if(this.swallowKeys){
28489             this.field.el.swallowEvent(['keydown','keypress']);
28490         }
28491         this.field.show();
28492         this.field.on("blur", this.onBlur, this);
28493         if(this.field.grow){
28494             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28495         }
28496     },
28497
28498     onSpecialKey : function(field, e)
28499     {
28500         //Roo.log('editor onSpecialKey');
28501         if(this.completeOnEnter && e.getKey() == e.ENTER){
28502             e.stopEvent();
28503             this.completeEdit();
28504             return;
28505         }
28506         // do not fire special key otherwise it might hide close the editor...
28507         if(e.getKey() == e.ENTER){    
28508             return;
28509         }
28510         if(this.cancelOnEsc && e.getKey() == e.ESC){
28511             this.cancelEdit();
28512             return;
28513         } 
28514         this.fireEvent('specialkey', field, e);
28515     
28516     },
28517
28518     /**
28519      * Starts the editing process and shows the editor.
28520      * @param {String/HTMLElement/Element} el The element to edit
28521      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28522       * to the innerHTML of el.
28523      */
28524     startEdit : function(el, value){
28525         if(this.editing){
28526             this.completeEdit();
28527         }
28528         this.boundEl = Roo.get(el);
28529         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28530         if(!this.rendered){
28531             this.render(this.parentEl || document.body);
28532         }
28533         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28534             return;
28535         }
28536         this.startValue = v;
28537         this.field.setValue(v);
28538         if(this.autoSize){
28539             var sz = this.boundEl.getSize();
28540             switch(this.autoSize){
28541                 case "width":
28542                 this.setSize(sz.width,  "");
28543                 break;
28544                 case "height":
28545                 this.setSize("",  sz.height);
28546                 break;
28547                 default:
28548                 this.setSize(sz.width,  sz.height);
28549             }
28550         }
28551         this.el.alignTo(this.boundEl, this.alignment);
28552         this.editing = true;
28553         if(Roo.QuickTips){
28554             Roo.QuickTips.disable();
28555         }
28556         this.show();
28557     },
28558
28559     /**
28560      * Sets the height and width of this editor.
28561      * @param {Number} width The new width
28562      * @param {Number} height The new height
28563      */
28564     setSize : function(w, h){
28565         this.field.setSize(w, h);
28566         if(this.el){
28567             this.el.sync();
28568         }
28569     },
28570
28571     /**
28572      * Realigns the editor to the bound field based on the current alignment config value.
28573      */
28574     realign : function(){
28575         this.el.alignTo(this.boundEl, this.alignment);
28576     },
28577
28578     /**
28579      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28580      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28581      */
28582     completeEdit : function(remainVisible){
28583         if(!this.editing){
28584             return;
28585         }
28586         var v = this.getValue();
28587         if(this.revertInvalid !== false && !this.field.isValid()){
28588             v = this.startValue;
28589             this.cancelEdit(true);
28590         }
28591         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28592             this.editing = false;
28593             this.hide();
28594             return;
28595         }
28596         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28597             this.editing = false;
28598             if(this.updateEl && this.boundEl){
28599                 this.boundEl.update(v);
28600             }
28601             if(remainVisible !== true){
28602                 this.hide();
28603             }
28604             this.fireEvent("complete", this, v, this.startValue);
28605         }
28606     },
28607
28608     // private
28609     onShow : function(){
28610         this.el.show();
28611         if(this.hideEl !== false){
28612             this.boundEl.hide();
28613         }
28614         this.field.show();
28615         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28616             this.fixIEFocus = true;
28617             this.deferredFocus.defer(50, this);
28618         }else{
28619             this.field.focus();
28620         }
28621         this.fireEvent("startedit", this.boundEl, this.startValue);
28622     },
28623
28624     deferredFocus : function(){
28625         if(this.editing){
28626             this.field.focus();
28627         }
28628     },
28629
28630     /**
28631      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28632      * reverted to the original starting value.
28633      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28634      * cancel (defaults to false)
28635      */
28636     cancelEdit : function(remainVisible){
28637         if(this.editing){
28638             this.setValue(this.startValue);
28639             if(remainVisible !== true){
28640                 this.hide();
28641             }
28642         }
28643     },
28644
28645     // private
28646     onBlur : function(){
28647         if(this.allowBlur !== true && this.editing){
28648             this.completeEdit();
28649         }
28650     },
28651
28652     // private
28653     onHide : function(){
28654         if(this.editing){
28655             this.completeEdit();
28656             return;
28657         }
28658         this.field.blur();
28659         if(this.field.collapse){
28660             this.field.collapse();
28661         }
28662         this.el.hide();
28663         if(this.hideEl !== false){
28664             this.boundEl.show();
28665         }
28666         if(Roo.QuickTips){
28667             Roo.QuickTips.enable();
28668         }
28669     },
28670
28671     /**
28672      * Sets the data value of the editor
28673      * @param {Mixed} value Any valid value supported by the underlying field
28674      */
28675     setValue : function(v){
28676         this.field.setValue(v);
28677     },
28678
28679     /**
28680      * Gets the data value of the editor
28681      * @return {Mixed} The data value
28682      */
28683     getValue : function(){
28684         return this.field.getValue();
28685     }
28686 });/*
28687  * Based on:
28688  * Ext JS Library 1.1.1
28689  * Copyright(c) 2006-2007, Ext JS, LLC.
28690  *
28691  * Originally Released Under LGPL - original licence link has changed is not relivant.
28692  *
28693  * Fork - LGPL
28694  * <script type="text/javascript">
28695  */
28696  
28697 /**
28698  * @class Roo.BasicDialog
28699  * @extends Roo.util.Observable
28700  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28701  * <pre><code>
28702 var dlg = new Roo.BasicDialog("my-dlg", {
28703     height: 200,
28704     width: 300,
28705     minHeight: 100,
28706     minWidth: 150,
28707     modal: true,
28708     proxyDrag: true,
28709     shadow: true
28710 });
28711 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28712 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28713 dlg.addButton('Cancel', dlg.hide, dlg);
28714 dlg.show();
28715 </code></pre>
28716   <b>A Dialog should always be a direct child of the body element.</b>
28717  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28718  * @cfg {String} title Default text to display in the title bar (defaults to null)
28719  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28720  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28721  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28722  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28723  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28724  * (defaults to null with no animation)
28725  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28726  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28727  * property for valid values (defaults to 'all')
28728  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28729  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28730  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28731  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28732  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28733  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28734  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28735  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28736  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28737  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28738  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28739  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28740  * draggable = true (defaults to false)
28741  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28742  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28743  * shadow (defaults to false)
28744  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28745  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28746  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28747  * @cfg {Array} buttons Array of buttons
28748  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28749  * @constructor
28750  * Create a new BasicDialog.
28751  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28752  * @param {Object} config Configuration options
28753  */
28754 Roo.BasicDialog = function(el, config){
28755     this.el = Roo.get(el);
28756     var dh = Roo.DomHelper;
28757     if(!this.el && config && config.autoCreate){
28758         if(typeof config.autoCreate == "object"){
28759             if(!config.autoCreate.id){
28760                 config.autoCreate.id = el;
28761             }
28762             this.el = dh.append(document.body,
28763                         config.autoCreate, true);
28764         }else{
28765             this.el = dh.append(document.body,
28766                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28767         }
28768     }
28769     el = this.el;
28770     el.setDisplayed(true);
28771     el.hide = this.hideAction;
28772     this.id = el.id;
28773     el.addClass("x-dlg");
28774
28775     Roo.apply(this, config);
28776
28777     this.proxy = el.createProxy("x-dlg-proxy");
28778     this.proxy.hide = this.hideAction;
28779     this.proxy.setOpacity(.5);
28780     this.proxy.hide();
28781
28782     if(config.width){
28783         el.setWidth(config.width);
28784     }
28785     if(config.height){
28786         el.setHeight(config.height);
28787     }
28788     this.size = el.getSize();
28789     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28790         this.xy = [config.x,config.y];
28791     }else{
28792         this.xy = el.getCenterXY(true);
28793     }
28794     /** The header element @type Roo.Element */
28795     this.header = el.child("> .x-dlg-hd");
28796     /** The body element @type Roo.Element */
28797     this.body = el.child("> .x-dlg-bd");
28798     /** The footer element @type Roo.Element */
28799     this.footer = el.child("> .x-dlg-ft");
28800
28801     if(!this.header){
28802         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28803     }
28804     if(!this.body){
28805         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28806     }
28807
28808     this.header.unselectable();
28809     if(this.title){
28810         this.header.update(this.title);
28811     }
28812     // this element allows the dialog to be focused for keyboard event
28813     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28814     this.focusEl.swallowEvent("click", true);
28815
28816     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28817
28818     // wrap the body and footer for special rendering
28819     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28820     if(this.footer){
28821         this.bwrap.dom.appendChild(this.footer.dom);
28822     }
28823
28824     this.bg = this.el.createChild({
28825         tag: "div", cls:"x-dlg-bg",
28826         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28827     });
28828     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28829
28830
28831     if(this.autoScroll !== false && !this.autoTabs){
28832         this.body.setStyle("overflow", "auto");
28833     }
28834
28835     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28836
28837     if(this.closable !== false){
28838         this.el.addClass("x-dlg-closable");
28839         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28840         this.close.on("click", this.closeClick, this);
28841         this.close.addClassOnOver("x-dlg-close-over");
28842     }
28843     if(this.collapsible !== false){
28844         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28845         this.collapseBtn.on("click", this.collapseClick, this);
28846         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28847         this.header.on("dblclick", this.collapseClick, this);
28848     }
28849     if(this.resizable !== false){
28850         this.el.addClass("x-dlg-resizable");
28851         this.resizer = new Roo.Resizable(el, {
28852             minWidth: this.minWidth || 80,
28853             minHeight:this.minHeight || 80,
28854             handles: this.resizeHandles || "all",
28855             pinned: true
28856         });
28857         this.resizer.on("beforeresize", this.beforeResize, this);
28858         this.resizer.on("resize", this.onResize, this);
28859     }
28860     if(this.draggable !== false){
28861         el.addClass("x-dlg-draggable");
28862         if (!this.proxyDrag) {
28863             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28864         }
28865         else {
28866             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28867         }
28868         dd.setHandleElId(this.header.id);
28869         dd.endDrag = this.endMove.createDelegate(this);
28870         dd.startDrag = this.startMove.createDelegate(this);
28871         dd.onDrag = this.onDrag.createDelegate(this);
28872         dd.scroll = false;
28873         this.dd = dd;
28874     }
28875     if(this.modal){
28876         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28877         this.mask.enableDisplayMode("block");
28878         this.mask.hide();
28879         this.el.addClass("x-dlg-modal");
28880     }
28881     if(this.shadow){
28882         this.shadow = new Roo.Shadow({
28883             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28884             offset : this.shadowOffset
28885         });
28886     }else{
28887         this.shadowOffset = 0;
28888     }
28889     if(Roo.useShims && this.shim !== false){
28890         this.shim = this.el.createShim();
28891         this.shim.hide = this.hideAction;
28892         this.shim.hide();
28893     }else{
28894         this.shim = false;
28895     }
28896     if(this.autoTabs){
28897         this.initTabs();
28898     }
28899     if (this.buttons) { 
28900         var bts= this.buttons;
28901         this.buttons = [];
28902         Roo.each(bts, function(b) {
28903             this.addButton(b);
28904         }, this);
28905     }
28906     
28907     
28908     this.addEvents({
28909         /**
28910          * @event keydown
28911          * Fires when a key is pressed
28912          * @param {Roo.BasicDialog} this
28913          * @param {Roo.EventObject} e
28914          */
28915         "keydown" : true,
28916         /**
28917          * @event move
28918          * Fires when this dialog is moved by the user.
28919          * @param {Roo.BasicDialog} this
28920          * @param {Number} x The new page X
28921          * @param {Number} y The new page Y
28922          */
28923         "move" : true,
28924         /**
28925          * @event resize
28926          * Fires when this dialog is resized by the user.
28927          * @param {Roo.BasicDialog} this
28928          * @param {Number} width The new width
28929          * @param {Number} height The new height
28930          */
28931         "resize" : true,
28932         /**
28933          * @event beforehide
28934          * Fires before this dialog is hidden.
28935          * @param {Roo.BasicDialog} this
28936          */
28937         "beforehide" : true,
28938         /**
28939          * @event hide
28940          * Fires when this dialog is hidden.
28941          * @param {Roo.BasicDialog} this
28942          */
28943         "hide" : true,
28944         /**
28945          * @event beforeshow
28946          * Fires before this dialog is shown.
28947          * @param {Roo.BasicDialog} this
28948          */
28949         "beforeshow" : true,
28950         /**
28951          * @event show
28952          * Fires when this dialog is shown.
28953          * @param {Roo.BasicDialog} this
28954          */
28955         "show" : true
28956     });
28957     el.on("keydown", this.onKeyDown, this);
28958     el.on("mousedown", this.toFront, this);
28959     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28960     this.el.hide();
28961     Roo.DialogManager.register(this);
28962     Roo.BasicDialog.superclass.constructor.call(this);
28963 };
28964
28965 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28966     shadowOffset: Roo.isIE ? 6 : 5,
28967     minHeight: 80,
28968     minWidth: 200,
28969     minButtonWidth: 75,
28970     defaultButton: null,
28971     buttonAlign: "right",
28972     tabTag: 'div',
28973     firstShow: true,
28974
28975     /**
28976      * Sets the dialog title text
28977      * @param {String} text The title text to display
28978      * @return {Roo.BasicDialog} this
28979      */
28980     setTitle : function(text){
28981         this.header.update(text);
28982         return this;
28983     },
28984
28985     // private
28986     closeClick : function(){
28987         this.hide();
28988     },
28989
28990     // private
28991     collapseClick : function(){
28992         this[this.collapsed ? "expand" : "collapse"]();
28993     },
28994
28995     /**
28996      * Collapses the dialog to its minimized state (only the title bar is visible).
28997      * Equivalent to the user clicking the collapse dialog button.
28998      */
28999     collapse : function(){
29000         if(!this.collapsed){
29001             this.collapsed = true;
29002             this.el.addClass("x-dlg-collapsed");
29003             this.restoreHeight = this.el.getHeight();
29004             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29005         }
29006     },
29007
29008     /**
29009      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29010      * clicking the expand dialog button.
29011      */
29012     expand : function(){
29013         if(this.collapsed){
29014             this.collapsed = false;
29015             this.el.removeClass("x-dlg-collapsed");
29016             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29017         }
29018     },
29019
29020     /**
29021      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29022      * @return {Roo.TabPanel} The tabs component
29023      */
29024     initTabs : function(){
29025         var tabs = this.getTabs();
29026         while(tabs.getTab(0)){
29027             tabs.removeTab(0);
29028         }
29029         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29030             var dom = el.dom;
29031             tabs.addTab(Roo.id(dom), dom.title);
29032             dom.title = "";
29033         });
29034         tabs.activate(0);
29035         return tabs;
29036     },
29037
29038     // private
29039     beforeResize : function(){
29040         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29041     },
29042
29043     // private
29044     onResize : function(){
29045         this.refreshSize();
29046         this.syncBodyHeight();
29047         this.adjustAssets();
29048         this.focus();
29049         this.fireEvent("resize", this, this.size.width, this.size.height);
29050     },
29051
29052     // private
29053     onKeyDown : function(e){
29054         if(this.isVisible()){
29055             this.fireEvent("keydown", this, e);
29056         }
29057     },
29058
29059     /**
29060      * Resizes the dialog.
29061      * @param {Number} width
29062      * @param {Number} height
29063      * @return {Roo.BasicDialog} this
29064      */
29065     resizeTo : function(width, height){
29066         this.el.setSize(width, height);
29067         this.size = {width: width, height: height};
29068         this.syncBodyHeight();
29069         if(this.fixedcenter){
29070             this.center();
29071         }
29072         if(this.isVisible()){
29073             this.constrainXY();
29074             this.adjustAssets();
29075         }
29076         this.fireEvent("resize", this, width, height);
29077         return this;
29078     },
29079
29080
29081     /**
29082      * Resizes the dialog to fit the specified content size.
29083      * @param {Number} width
29084      * @param {Number} height
29085      * @return {Roo.BasicDialog} this
29086      */
29087     setContentSize : function(w, h){
29088         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29089         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29090         //if(!this.el.isBorderBox()){
29091             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29092             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29093         //}
29094         if(this.tabs){
29095             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29096             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29097         }
29098         this.resizeTo(w, h);
29099         return this;
29100     },
29101
29102     /**
29103      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29104      * executed in response to a particular key being pressed while the dialog is active.
29105      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29106      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29107      * @param {Function} fn The function to call
29108      * @param {Object} scope (optional) The scope of the function
29109      * @return {Roo.BasicDialog} this
29110      */
29111     addKeyListener : function(key, fn, scope){
29112         var keyCode, shift, ctrl, alt;
29113         if(typeof key == "object" && !(key instanceof Array)){
29114             keyCode = key["key"];
29115             shift = key["shift"];
29116             ctrl = key["ctrl"];
29117             alt = key["alt"];
29118         }else{
29119             keyCode = key;
29120         }
29121         var handler = function(dlg, e){
29122             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29123                 var k = e.getKey();
29124                 if(keyCode instanceof Array){
29125                     for(var i = 0, len = keyCode.length; i < len; i++){
29126                         if(keyCode[i] == k){
29127                           fn.call(scope || window, dlg, k, e);
29128                           return;
29129                         }
29130                     }
29131                 }else{
29132                     if(k == keyCode){
29133                         fn.call(scope || window, dlg, k, e);
29134                     }
29135                 }
29136             }
29137         };
29138         this.on("keydown", handler);
29139         return this;
29140     },
29141
29142     /**
29143      * Returns the TabPanel component (creates it if it doesn't exist).
29144      * Note: If you wish to simply check for the existence of tabs without creating them,
29145      * check for a null 'tabs' property.
29146      * @return {Roo.TabPanel} The tabs component
29147      */
29148     getTabs : function(){
29149         if(!this.tabs){
29150             this.el.addClass("x-dlg-auto-tabs");
29151             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29152             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29153         }
29154         return this.tabs;
29155     },
29156
29157     /**
29158      * Adds a button to the footer section of the dialog.
29159      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29160      * object or a valid Roo.DomHelper element config
29161      * @param {Function} handler The function called when the button is clicked
29162      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29163      * @return {Roo.Button} The new button
29164      */
29165     addButton : function(config, handler, scope){
29166         var dh = Roo.DomHelper;
29167         if(!this.footer){
29168             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29169         }
29170         if(!this.btnContainer){
29171             var tb = this.footer.createChild({
29172
29173                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29174                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29175             }, null, true);
29176             this.btnContainer = tb.firstChild.firstChild.firstChild;
29177         }
29178         var bconfig = {
29179             handler: handler,
29180             scope: scope,
29181             minWidth: this.minButtonWidth,
29182             hideParent:true
29183         };
29184         if(typeof config == "string"){
29185             bconfig.text = config;
29186         }else{
29187             if(config.tag){
29188                 bconfig.dhconfig = config;
29189             }else{
29190                 Roo.apply(bconfig, config);
29191             }
29192         }
29193         var fc = false;
29194         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29195             bconfig.position = Math.max(0, bconfig.position);
29196             fc = this.btnContainer.childNodes[bconfig.position];
29197         }
29198          
29199         var btn = new Roo.Button(
29200             fc ? 
29201                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29202                 : this.btnContainer.appendChild(document.createElement("td")),
29203             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29204             bconfig
29205         );
29206         this.syncBodyHeight();
29207         if(!this.buttons){
29208             /**
29209              * Array of all the buttons that have been added to this dialog via addButton
29210              * @type Array
29211              */
29212             this.buttons = [];
29213         }
29214         this.buttons.push(btn);
29215         return btn;
29216     },
29217
29218     /**
29219      * Sets the default button to be focused when the dialog is displayed.
29220      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29221      * @return {Roo.BasicDialog} this
29222      */
29223     setDefaultButton : function(btn){
29224         this.defaultButton = btn;
29225         return this;
29226     },
29227
29228     // private
29229     getHeaderFooterHeight : function(safe){
29230         var height = 0;
29231         if(this.header){
29232            height += this.header.getHeight();
29233         }
29234         if(this.footer){
29235            var fm = this.footer.getMargins();
29236             height += (this.footer.getHeight()+fm.top+fm.bottom);
29237         }
29238         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29239         height += this.centerBg.getPadding("tb");
29240         return height;
29241     },
29242
29243     // private
29244     syncBodyHeight : function(){
29245         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29246         var height = this.size.height - this.getHeaderFooterHeight(false);
29247         bd.setHeight(height-bd.getMargins("tb"));
29248         var hh = this.header.getHeight();
29249         var h = this.size.height-hh;
29250         cb.setHeight(h);
29251         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29252         bw.setHeight(h-cb.getPadding("tb"));
29253         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29254         bd.setWidth(bw.getWidth(true));
29255         if(this.tabs){
29256             this.tabs.syncHeight();
29257             if(Roo.isIE){
29258                 this.tabs.el.repaint();
29259             }
29260         }
29261     },
29262
29263     /**
29264      * Restores the previous state of the dialog if Roo.state is configured.
29265      * @return {Roo.BasicDialog} this
29266      */
29267     restoreState : function(){
29268         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29269         if(box && box.width){
29270             this.xy = [box.x, box.y];
29271             this.resizeTo(box.width, box.height);
29272         }
29273         return this;
29274     },
29275
29276     // private
29277     beforeShow : function(){
29278         this.expand();
29279         if(this.fixedcenter){
29280             this.xy = this.el.getCenterXY(true);
29281         }
29282         if(this.modal){
29283             Roo.get(document.body).addClass("x-body-masked");
29284             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29285             this.mask.show();
29286         }
29287         this.constrainXY();
29288     },
29289
29290     // private
29291     animShow : function(){
29292         var b = Roo.get(this.animateTarget).getBox();
29293         this.proxy.setSize(b.width, b.height);
29294         this.proxy.setLocation(b.x, b.y);
29295         this.proxy.show();
29296         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29297                     true, .35, this.showEl.createDelegate(this));
29298     },
29299
29300     /**
29301      * Shows the dialog.
29302      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29303      * @return {Roo.BasicDialog} this
29304      */
29305     show : function(animateTarget){
29306         if (this.fireEvent("beforeshow", this) === false){
29307             return;
29308         }
29309         if(this.syncHeightBeforeShow){
29310             this.syncBodyHeight();
29311         }else if(this.firstShow){
29312             this.firstShow = false;
29313             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29314         }
29315         this.animateTarget = animateTarget || this.animateTarget;
29316         if(!this.el.isVisible()){
29317             this.beforeShow();
29318             if(this.animateTarget && Roo.get(this.animateTarget)){
29319                 this.animShow();
29320             }else{
29321                 this.showEl();
29322             }
29323         }
29324         return this;
29325     },
29326
29327     // private
29328     showEl : function(){
29329         this.proxy.hide();
29330         this.el.setXY(this.xy);
29331         this.el.show();
29332         this.adjustAssets(true);
29333         this.toFront();
29334         this.focus();
29335         // IE peekaboo bug - fix found by Dave Fenwick
29336         if(Roo.isIE){
29337             this.el.repaint();
29338         }
29339         this.fireEvent("show", this);
29340     },
29341
29342     /**
29343      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29344      * dialog itself will receive focus.
29345      */
29346     focus : function(){
29347         if(this.defaultButton){
29348             this.defaultButton.focus();
29349         }else{
29350             this.focusEl.focus();
29351         }
29352     },
29353
29354     // private
29355     constrainXY : function(){
29356         if(this.constraintoviewport !== false){
29357             if(!this.viewSize){
29358                 if(this.container){
29359                     var s = this.container.getSize();
29360                     this.viewSize = [s.width, s.height];
29361                 }else{
29362                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29363                 }
29364             }
29365             var s = Roo.get(this.container||document).getScroll();
29366
29367             var x = this.xy[0], y = this.xy[1];
29368             var w = this.size.width, h = this.size.height;
29369             var vw = this.viewSize[0], vh = this.viewSize[1];
29370             // only move it if it needs it
29371             var moved = false;
29372             // first validate right/bottom
29373             if(x + w > vw+s.left){
29374                 x = vw - w;
29375                 moved = true;
29376             }
29377             if(y + h > vh+s.top){
29378                 y = vh - h;
29379                 moved = true;
29380             }
29381             // then make sure top/left isn't negative
29382             if(x < s.left){
29383                 x = s.left;
29384                 moved = true;
29385             }
29386             if(y < s.top){
29387                 y = s.top;
29388                 moved = true;
29389             }
29390             if(moved){
29391                 // cache xy
29392                 this.xy = [x, y];
29393                 if(this.isVisible()){
29394                     this.el.setLocation(x, y);
29395                     this.adjustAssets();
29396                 }
29397             }
29398         }
29399     },
29400
29401     // private
29402     onDrag : function(){
29403         if(!this.proxyDrag){
29404             this.xy = this.el.getXY();
29405             this.adjustAssets();
29406         }
29407     },
29408
29409     // private
29410     adjustAssets : function(doShow){
29411         var x = this.xy[0], y = this.xy[1];
29412         var w = this.size.width, h = this.size.height;
29413         if(doShow === true){
29414             if(this.shadow){
29415                 this.shadow.show(this.el);
29416             }
29417             if(this.shim){
29418                 this.shim.show();
29419             }
29420         }
29421         if(this.shadow && this.shadow.isVisible()){
29422             this.shadow.show(this.el);
29423         }
29424         if(this.shim && this.shim.isVisible()){
29425             this.shim.setBounds(x, y, w, h);
29426         }
29427     },
29428
29429     // private
29430     adjustViewport : function(w, h){
29431         if(!w || !h){
29432             w = Roo.lib.Dom.getViewWidth();
29433             h = Roo.lib.Dom.getViewHeight();
29434         }
29435         // cache the size
29436         this.viewSize = [w, h];
29437         if(this.modal && this.mask.isVisible()){
29438             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29439             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29440         }
29441         if(this.isVisible()){
29442             this.constrainXY();
29443         }
29444     },
29445
29446     /**
29447      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29448      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29449      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29450      */
29451     destroy : function(removeEl){
29452         if(this.isVisible()){
29453             this.animateTarget = null;
29454             this.hide();
29455         }
29456         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29457         if(this.tabs){
29458             this.tabs.destroy(removeEl);
29459         }
29460         Roo.destroy(
29461              this.shim,
29462              this.proxy,
29463              this.resizer,
29464              this.close,
29465              this.mask
29466         );
29467         if(this.dd){
29468             this.dd.unreg();
29469         }
29470         if(this.buttons){
29471            for(var i = 0, len = this.buttons.length; i < len; i++){
29472                this.buttons[i].destroy();
29473            }
29474         }
29475         this.el.removeAllListeners();
29476         if(removeEl === true){
29477             this.el.update("");
29478             this.el.remove();
29479         }
29480         Roo.DialogManager.unregister(this);
29481     },
29482
29483     // private
29484     startMove : function(){
29485         if(this.proxyDrag){
29486             this.proxy.show();
29487         }
29488         if(this.constraintoviewport !== false){
29489             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29490         }
29491     },
29492
29493     // private
29494     endMove : function(){
29495         if(!this.proxyDrag){
29496             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29497         }else{
29498             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29499             this.proxy.hide();
29500         }
29501         this.refreshSize();
29502         this.adjustAssets();
29503         this.focus();
29504         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29505     },
29506
29507     /**
29508      * Brings this dialog to the front of any other visible dialogs
29509      * @return {Roo.BasicDialog} this
29510      */
29511     toFront : function(){
29512         Roo.DialogManager.bringToFront(this);
29513         return this;
29514     },
29515
29516     /**
29517      * Sends this dialog to the back (under) of any other visible dialogs
29518      * @return {Roo.BasicDialog} this
29519      */
29520     toBack : function(){
29521         Roo.DialogManager.sendToBack(this);
29522         return this;
29523     },
29524
29525     /**
29526      * Centers this dialog in the viewport
29527      * @return {Roo.BasicDialog} this
29528      */
29529     center : function(){
29530         var xy = this.el.getCenterXY(true);
29531         this.moveTo(xy[0], xy[1]);
29532         return this;
29533     },
29534
29535     /**
29536      * Moves the dialog's top-left corner to the specified point
29537      * @param {Number} x
29538      * @param {Number} y
29539      * @return {Roo.BasicDialog} this
29540      */
29541     moveTo : function(x, y){
29542         this.xy = [x,y];
29543         if(this.isVisible()){
29544             this.el.setXY(this.xy);
29545             this.adjustAssets();
29546         }
29547         return this;
29548     },
29549
29550     /**
29551      * Aligns the dialog to the specified element
29552      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29553      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29554      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29555      * @return {Roo.BasicDialog} this
29556      */
29557     alignTo : function(element, position, offsets){
29558         this.xy = this.el.getAlignToXY(element, position, offsets);
29559         if(this.isVisible()){
29560             this.el.setXY(this.xy);
29561             this.adjustAssets();
29562         }
29563         return this;
29564     },
29565
29566     /**
29567      * Anchors an element to another element and realigns it when the window is resized.
29568      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29569      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29570      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29571      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29572      * is a number, it is used as the buffer delay (defaults to 50ms).
29573      * @return {Roo.BasicDialog} this
29574      */
29575     anchorTo : function(el, alignment, offsets, monitorScroll){
29576         var action = function(){
29577             this.alignTo(el, alignment, offsets);
29578         };
29579         Roo.EventManager.onWindowResize(action, this);
29580         var tm = typeof monitorScroll;
29581         if(tm != 'undefined'){
29582             Roo.EventManager.on(window, 'scroll', action, this,
29583                 {buffer: tm == 'number' ? monitorScroll : 50});
29584         }
29585         action.call(this);
29586         return this;
29587     },
29588
29589     /**
29590      * Returns true if the dialog is visible
29591      * @return {Boolean}
29592      */
29593     isVisible : function(){
29594         return this.el.isVisible();
29595     },
29596
29597     // private
29598     animHide : function(callback){
29599         var b = Roo.get(this.animateTarget).getBox();
29600         this.proxy.show();
29601         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29602         this.el.hide();
29603         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29604                     this.hideEl.createDelegate(this, [callback]));
29605     },
29606
29607     /**
29608      * Hides the dialog.
29609      * @param {Function} callback (optional) Function to call when the dialog is hidden
29610      * @return {Roo.BasicDialog} this
29611      */
29612     hide : function(callback){
29613         if (this.fireEvent("beforehide", this) === false){
29614             return;
29615         }
29616         if(this.shadow){
29617             this.shadow.hide();
29618         }
29619         if(this.shim) {
29620           this.shim.hide();
29621         }
29622         // sometimes animateTarget seems to get set.. causing problems...
29623         // this just double checks..
29624         if(this.animateTarget && Roo.get(this.animateTarget)) {
29625            this.animHide(callback);
29626         }else{
29627             this.el.hide();
29628             this.hideEl(callback);
29629         }
29630         return this;
29631     },
29632
29633     // private
29634     hideEl : function(callback){
29635         this.proxy.hide();
29636         if(this.modal){
29637             this.mask.hide();
29638             Roo.get(document.body).removeClass("x-body-masked");
29639         }
29640         this.fireEvent("hide", this);
29641         if(typeof callback == "function"){
29642             callback();
29643         }
29644     },
29645
29646     // private
29647     hideAction : function(){
29648         this.setLeft("-10000px");
29649         this.setTop("-10000px");
29650         this.setStyle("visibility", "hidden");
29651     },
29652
29653     // private
29654     refreshSize : function(){
29655         this.size = this.el.getSize();
29656         this.xy = this.el.getXY();
29657         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29658     },
29659
29660     // private
29661     // z-index is managed by the DialogManager and may be overwritten at any time
29662     setZIndex : function(index){
29663         if(this.modal){
29664             this.mask.setStyle("z-index", index);
29665         }
29666         if(this.shim){
29667             this.shim.setStyle("z-index", ++index);
29668         }
29669         if(this.shadow){
29670             this.shadow.setZIndex(++index);
29671         }
29672         this.el.setStyle("z-index", ++index);
29673         if(this.proxy){
29674             this.proxy.setStyle("z-index", ++index);
29675         }
29676         if(this.resizer){
29677             this.resizer.proxy.setStyle("z-index", ++index);
29678         }
29679
29680         this.lastZIndex = index;
29681     },
29682
29683     /**
29684      * Returns the element for this dialog
29685      * @return {Roo.Element} The underlying dialog Element
29686      */
29687     getEl : function(){
29688         return this.el;
29689     }
29690 });
29691
29692 /**
29693  * @class Roo.DialogManager
29694  * Provides global access to BasicDialogs that have been created and
29695  * support for z-indexing (layering) multiple open dialogs.
29696  */
29697 Roo.DialogManager = function(){
29698     var list = {};
29699     var accessList = [];
29700     var front = null;
29701
29702     // private
29703     var sortDialogs = function(d1, d2){
29704         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29705     };
29706
29707     // private
29708     var orderDialogs = function(){
29709         accessList.sort(sortDialogs);
29710         var seed = Roo.DialogManager.zseed;
29711         for(var i = 0, len = accessList.length; i < len; i++){
29712             var dlg = accessList[i];
29713             if(dlg){
29714                 dlg.setZIndex(seed + (i*10));
29715             }
29716         }
29717     };
29718
29719     return {
29720         /**
29721          * The starting z-index for BasicDialogs (defaults to 9000)
29722          * @type Number The z-index value
29723          */
29724         zseed : 9000,
29725
29726         // private
29727         register : function(dlg){
29728             list[dlg.id] = dlg;
29729             accessList.push(dlg);
29730         },
29731
29732         // private
29733         unregister : function(dlg){
29734             delete list[dlg.id];
29735             var i=0;
29736             var len=0;
29737             if(!accessList.indexOf){
29738                 for(  i = 0, len = accessList.length; i < len; i++){
29739                     if(accessList[i] == dlg){
29740                         accessList.splice(i, 1);
29741                         return;
29742                     }
29743                 }
29744             }else{
29745                  i = accessList.indexOf(dlg);
29746                 if(i != -1){
29747                     accessList.splice(i, 1);
29748                 }
29749             }
29750         },
29751
29752         /**
29753          * Gets a registered dialog by id
29754          * @param {String/Object} id The id of the dialog or a dialog
29755          * @return {Roo.BasicDialog} this
29756          */
29757         get : function(id){
29758             return typeof id == "object" ? id : list[id];
29759         },
29760
29761         /**
29762          * Brings the specified dialog to the front
29763          * @param {String/Object} dlg The id of the dialog or a dialog
29764          * @return {Roo.BasicDialog} this
29765          */
29766         bringToFront : function(dlg){
29767             dlg = this.get(dlg);
29768             if(dlg != front){
29769                 front = dlg;
29770                 dlg._lastAccess = new Date().getTime();
29771                 orderDialogs();
29772             }
29773             return dlg;
29774         },
29775
29776         /**
29777          * Sends the specified dialog to the back
29778          * @param {String/Object} dlg The id of the dialog or a dialog
29779          * @return {Roo.BasicDialog} this
29780          */
29781         sendToBack : function(dlg){
29782             dlg = this.get(dlg);
29783             dlg._lastAccess = -(new Date().getTime());
29784             orderDialogs();
29785             return dlg;
29786         },
29787
29788         /**
29789          * Hides all dialogs
29790          */
29791         hideAll : function(){
29792             for(var id in list){
29793                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29794                     list[id].hide();
29795                 }
29796             }
29797         }
29798     };
29799 }();
29800
29801 /**
29802  * @class Roo.LayoutDialog
29803  * @extends Roo.BasicDialog
29804  * Dialog which provides adjustments for working with a layout in a Dialog.
29805  * Add your necessary layout config options to the dialog's config.<br>
29806  * Example usage (including a nested layout):
29807  * <pre><code>
29808 if(!dialog){
29809     dialog = new Roo.LayoutDialog("download-dlg", {
29810         modal: true,
29811         width:600,
29812         height:450,
29813         shadow:true,
29814         minWidth:500,
29815         minHeight:350,
29816         autoTabs:true,
29817         proxyDrag:true,
29818         // layout config merges with the dialog config
29819         center:{
29820             tabPosition: "top",
29821             alwaysShowTabs: true
29822         }
29823     });
29824     dialog.addKeyListener(27, dialog.hide, dialog);
29825     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29826     dialog.addButton("Build It!", this.getDownload, this);
29827
29828     // we can even add nested layouts
29829     var innerLayout = new Roo.BorderLayout("dl-inner", {
29830         east: {
29831             initialSize: 200,
29832             autoScroll:true,
29833             split:true
29834         },
29835         center: {
29836             autoScroll:true
29837         }
29838     });
29839     innerLayout.beginUpdate();
29840     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29841     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29842     innerLayout.endUpdate(true);
29843
29844     var layout = dialog.getLayout();
29845     layout.beginUpdate();
29846     layout.add("center", new Roo.ContentPanel("standard-panel",
29847                         {title: "Download the Source", fitToFrame:true}));
29848     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29849                {title: "Build your own roo.js"}));
29850     layout.getRegion("center").showPanel(sp);
29851     layout.endUpdate();
29852 }
29853 </code></pre>
29854     * @constructor
29855     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29856     * @param {Object} config configuration options
29857   */
29858 Roo.LayoutDialog = function(el, cfg){
29859     
29860     var config=  cfg;
29861     if (typeof(cfg) == 'undefined') {
29862         config = Roo.apply({}, el);
29863         // not sure why we use documentElement here.. - it should always be body.
29864         // IE7 borks horribly if we use documentElement.
29865         // webkit also does not like documentElement - it creates a body element...
29866         el = Roo.get( document.body || document.documentElement ).createChild();
29867         //config.autoCreate = true;
29868     }
29869     
29870     
29871     config.autoTabs = false;
29872     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29873     this.body.setStyle({overflow:"hidden", position:"relative"});
29874     this.layout = new Roo.BorderLayout(this.body.dom, config);
29875     this.layout.monitorWindowResize = false;
29876     this.el.addClass("x-dlg-auto-layout");
29877     // fix case when center region overwrites center function
29878     this.center = Roo.BasicDialog.prototype.center;
29879     this.on("show", this.layout.layout, this.layout, true);
29880     if (config.items) {
29881         var xitems = config.items;
29882         delete config.items;
29883         Roo.each(xitems, this.addxtype, this);
29884     }
29885     
29886     
29887 };
29888 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29889     /**
29890      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29891      * @deprecated
29892      */
29893     endUpdate : function(){
29894         this.layout.endUpdate();
29895     },
29896
29897     /**
29898      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29899      *  @deprecated
29900      */
29901     beginUpdate : function(){
29902         this.layout.beginUpdate();
29903     },
29904
29905     /**
29906      * Get the BorderLayout for this dialog
29907      * @return {Roo.BorderLayout}
29908      */
29909     getLayout : function(){
29910         return this.layout;
29911     },
29912
29913     showEl : function(){
29914         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29915         if(Roo.isIE7){
29916             this.layout.layout();
29917         }
29918     },
29919
29920     // private
29921     // Use the syncHeightBeforeShow config option to control this automatically
29922     syncBodyHeight : function(){
29923         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29924         if(this.layout){this.layout.layout();}
29925     },
29926     
29927       /**
29928      * Add an xtype element (actually adds to the layout.)
29929      * @return {Object} xdata xtype object data.
29930      */
29931     
29932     addxtype : function(c) {
29933         return this.layout.addxtype(c);
29934     }
29935 });/*
29936  * Based on:
29937  * Ext JS Library 1.1.1
29938  * Copyright(c) 2006-2007, Ext JS, LLC.
29939  *
29940  * Originally Released Under LGPL - original licence link has changed is not relivant.
29941  *
29942  * Fork - LGPL
29943  * <script type="text/javascript">
29944  */
29945  
29946 /**
29947  * @class Roo.MessageBox
29948  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29949  * Example usage:
29950  *<pre><code>
29951 // Basic alert:
29952 Roo.Msg.alert('Status', 'Changes saved successfully.');
29953
29954 // Prompt for user data:
29955 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29956     if (btn == 'ok'){
29957         // process text value...
29958     }
29959 });
29960
29961 // Show a dialog using config options:
29962 Roo.Msg.show({
29963    title:'Save Changes?',
29964    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29965    buttons: Roo.Msg.YESNOCANCEL,
29966    fn: processResult,
29967    animEl: 'elId'
29968 });
29969 </code></pre>
29970  * @singleton
29971  */
29972 Roo.MessageBox = function(){
29973     var dlg, opt, mask, waitTimer;
29974     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29975     var buttons, activeTextEl, bwidth;
29976
29977     // private
29978     var handleButton = function(button){
29979         dlg.hide();
29980         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29981     };
29982
29983     // private
29984     var handleHide = function(){
29985         if(opt && opt.cls){
29986             dlg.el.removeClass(opt.cls);
29987         }
29988         if(waitTimer){
29989             Roo.TaskMgr.stop(waitTimer);
29990             waitTimer = null;
29991         }
29992     };
29993
29994     // private
29995     var updateButtons = function(b){
29996         var width = 0;
29997         if(!b){
29998             buttons["ok"].hide();
29999             buttons["cancel"].hide();
30000             buttons["yes"].hide();
30001             buttons["no"].hide();
30002             dlg.footer.dom.style.display = 'none';
30003             return width;
30004         }
30005         dlg.footer.dom.style.display = '';
30006         for(var k in buttons){
30007             if(typeof buttons[k] != "function"){
30008                 if(b[k]){
30009                     buttons[k].show();
30010                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30011                     width += buttons[k].el.getWidth()+15;
30012                 }else{
30013                     buttons[k].hide();
30014                 }
30015             }
30016         }
30017         return width;
30018     };
30019
30020     // private
30021     var handleEsc = function(d, k, e){
30022         if(opt && opt.closable !== false){
30023             dlg.hide();
30024         }
30025         if(e){
30026             e.stopEvent();
30027         }
30028     };
30029
30030     return {
30031         /**
30032          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30033          * @return {Roo.BasicDialog} The BasicDialog element
30034          */
30035         getDialog : function(){
30036            if(!dlg){
30037                 dlg = new Roo.BasicDialog("x-msg-box", {
30038                     autoCreate : true,
30039                     shadow: true,
30040                     draggable: true,
30041                     resizable:false,
30042                     constraintoviewport:false,
30043                     fixedcenter:true,
30044                     collapsible : false,
30045                     shim:true,
30046                     modal: true,
30047                     width:400, height:100,
30048                     buttonAlign:"center",
30049                     closeClick : function(){
30050                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30051                             handleButton("no");
30052                         }else{
30053                             handleButton("cancel");
30054                         }
30055                     }
30056                 });
30057                 dlg.on("hide", handleHide);
30058                 mask = dlg.mask;
30059                 dlg.addKeyListener(27, handleEsc);
30060                 buttons = {};
30061                 var bt = this.buttonText;
30062                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30063                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30064                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30065                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30066                 bodyEl = dlg.body.createChild({
30067
30068                     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>'
30069                 });
30070                 msgEl = bodyEl.dom.firstChild;
30071                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30072                 textboxEl.enableDisplayMode();
30073                 textboxEl.addKeyListener([10,13], function(){
30074                     if(dlg.isVisible() && opt && opt.buttons){
30075                         if(opt.buttons.ok){
30076                             handleButton("ok");
30077                         }else if(opt.buttons.yes){
30078                             handleButton("yes");
30079                         }
30080                     }
30081                 });
30082                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30083                 textareaEl.enableDisplayMode();
30084                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30085                 progressEl.enableDisplayMode();
30086                 var pf = progressEl.dom.firstChild;
30087                 if (pf) {
30088                     pp = Roo.get(pf.firstChild);
30089                     pp.setHeight(pf.offsetHeight);
30090                 }
30091                 
30092             }
30093             return dlg;
30094         },
30095
30096         /**
30097          * Updates the message box body text
30098          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30099          * the XHTML-compliant non-breaking space character '&amp;#160;')
30100          * @return {Roo.MessageBox} This message box
30101          */
30102         updateText : function(text){
30103             if(!dlg.isVisible() && !opt.width){
30104                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30105             }
30106             msgEl.innerHTML = text || '&#160;';
30107       
30108             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30109             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30110             var w = Math.max(
30111                     Math.min(opt.width || cw , this.maxWidth), 
30112                     Math.max(opt.minWidth || this.minWidth, bwidth)
30113             );
30114             if(opt.prompt){
30115                 activeTextEl.setWidth(w);
30116             }
30117             if(dlg.isVisible()){
30118                 dlg.fixedcenter = false;
30119             }
30120             // to big, make it scoll.
30121             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30122                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30123                 bodyEl.dom.style.overflowY = 'auto !important';
30124             } else {
30125                 bodyEl.dom.style.height = '';
30126                 bodyEl.dom.style.overflowY = '';
30127             }
30128             if (cw > w) {
30129                 bodyEl.dom.style.overflowX = 'auto !important';
30130             } else {
30131                 bodyEl.dom.style.overflowX = '';
30132             }
30133             
30134             dlg.setContentSize(w, bodyEl.getHeight());
30135             if(dlg.isVisible()){
30136                 dlg.fixedcenter = true;
30137             }
30138             return this;
30139         },
30140
30141         /**
30142          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30143          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30144          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30145          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30146          * @return {Roo.MessageBox} This message box
30147          */
30148         updateProgress : function(value, text){
30149             if(text){
30150                 this.updateText(text);
30151             }
30152             if (pp) { // weird bug on my firefox - for some reason this is not defined
30153                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30154             }
30155             return this;
30156         },        
30157
30158         /**
30159          * Returns true if the message box is currently displayed
30160          * @return {Boolean} True if the message box is visible, else false
30161          */
30162         isVisible : function(){
30163             return dlg && dlg.isVisible();  
30164         },
30165
30166         /**
30167          * Hides the message box if it is displayed
30168          */
30169         hide : function(){
30170             if(this.isVisible()){
30171                 dlg.hide();
30172             }  
30173         },
30174
30175         /**
30176          * Displays a new message box, or reinitializes an existing message box, based on the config options
30177          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30178          * The following config object properties are supported:
30179          * <pre>
30180 Property    Type             Description
30181 ----------  ---------------  ------------------------------------------------------------------------------------
30182 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30183                                    closes (defaults to undefined)
30184 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30185                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30186 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30187                                    progress and wait dialogs will ignore this property and always hide the
30188                                    close button as they can only be closed programmatically.
30189 cls               String           A custom CSS class to apply to the message box element
30190 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30191                                    displayed (defaults to 75)
30192 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30193                                    function will be btn (the name of the button that was clicked, if applicable,
30194                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30195                                    Progress and wait dialogs will ignore this option since they do not respond to
30196                                    user actions and can only be closed programmatically, so any required function
30197                                    should be called by the same code after it closes the dialog.
30198 icon              String           A CSS class that provides a background image to be used as an icon for
30199                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30200 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30201 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30202 modal             Boolean          False to allow user interaction with the page while the message box is
30203                                    displayed (defaults to true)
30204 msg               String           A string that will replace the existing message box body text (defaults
30205                                    to the XHTML-compliant non-breaking space character '&#160;')
30206 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30207 progress          Boolean          True to display a progress bar (defaults to false)
30208 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30209 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30210 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30211 title             String           The title text
30212 value             String           The string value to set into the active textbox element if displayed
30213 wait              Boolean          True to display a progress bar (defaults to false)
30214 width             Number           The width of the dialog in pixels
30215 </pre>
30216          *
30217          * Example usage:
30218          * <pre><code>
30219 Roo.Msg.show({
30220    title: 'Address',
30221    msg: 'Please enter your address:',
30222    width: 300,
30223    buttons: Roo.MessageBox.OKCANCEL,
30224    multiline: true,
30225    fn: saveAddress,
30226    animEl: 'addAddressBtn'
30227 });
30228 </code></pre>
30229          * @param {Object} config Configuration options
30230          * @return {Roo.MessageBox} This message box
30231          */
30232         show : function(options)
30233         {
30234             
30235             // this causes nightmares if you show one dialog after another
30236             // especially on callbacks..
30237              
30238             if(this.isVisible()){
30239                 
30240                 this.hide();
30241                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30242                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30243                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30244                 
30245             }
30246             var d = this.getDialog();
30247             opt = options;
30248             d.setTitle(opt.title || "&#160;");
30249             d.close.setDisplayed(opt.closable !== false);
30250             activeTextEl = textboxEl;
30251             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30252             if(opt.prompt){
30253                 if(opt.multiline){
30254                     textboxEl.hide();
30255                     textareaEl.show();
30256                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30257                         opt.multiline : this.defaultTextHeight);
30258                     activeTextEl = textareaEl;
30259                 }else{
30260                     textboxEl.show();
30261                     textareaEl.hide();
30262                 }
30263             }else{
30264                 textboxEl.hide();
30265                 textareaEl.hide();
30266             }
30267             progressEl.setDisplayed(opt.progress === true);
30268             this.updateProgress(0);
30269             activeTextEl.dom.value = opt.value || "";
30270             if(opt.prompt){
30271                 dlg.setDefaultButton(activeTextEl);
30272             }else{
30273                 var bs = opt.buttons;
30274                 var db = null;
30275                 if(bs && bs.ok){
30276                     db = buttons["ok"];
30277                 }else if(bs && bs.yes){
30278                     db = buttons["yes"];
30279                 }
30280                 dlg.setDefaultButton(db);
30281             }
30282             bwidth = updateButtons(opt.buttons);
30283             this.updateText(opt.msg);
30284             if(opt.cls){
30285                 d.el.addClass(opt.cls);
30286             }
30287             d.proxyDrag = opt.proxyDrag === true;
30288             d.modal = opt.modal !== false;
30289             d.mask = opt.modal !== false ? mask : false;
30290             if(!d.isVisible()){
30291                 // force it to the end of the z-index stack so it gets a cursor in FF
30292                 document.body.appendChild(dlg.el.dom);
30293                 d.animateTarget = null;
30294                 d.show(options.animEl);
30295             }
30296             return this;
30297         },
30298
30299         /**
30300          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30301          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30302          * and closing the message box when the process is complete.
30303          * @param {String} title The title bar text
30304          * @param {String} msg The message box body text
30305          * @return {Roo.MessageBox} This message box
30306          */
30307         progress : function(title, msg){
30308             this.show({
30309                 title : title,
30310                 msg : msg,
30311                 buttons: false,
30312                 progress:true,
30313                 closable:false,
30314                 minWidth: this.minProgressWidth,
30315                 modal : true
30316             });
30317             return this;
30318         },
30319
30320         /**
30321          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30322          * If a callback function is passed it will be called after the user clicks the button, and the
30323          * id of the button that was clicked will be passed as the only parameter to the callback
30324          * (could also be the top-right close button).
30325          * @param {String} title The title bar text
30326          * @param {String} msg The message box body text
30327          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30328          * @param {Object} scope (optional) The scope of the callback function
30329          * @return {Roo.MessageBox} This message box
30330          */
30331         alert : function(title, msg, fn, scope){
30332             this.show({
30333                 title : title,
30334                 msg : msg,
30335                 buttons: this.OK,
30336                 fn: fn,
30337                 scope : scope,
30338                 modal : true
30339             });
30340             return this;
30341         },
30342
30343         /**
30344          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30345          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30346          * You are responsible for closing the message box when the process is complete.
30347          * @param {String} msg The message box body text
30348          * @param {String} title (optional) The title bar text
30349          * @return {Roo.MessageBox} This message box
30350          */
30351         wait : function(msg, title){
30352             this.show({
30353                 title : title,
30354                 msg : msg,
30355                 buttons: false,
30356                 closable:false,
30357                 progress:true,
30358                 modal:true,
30359                 width:300,
30360                 wait:true
30361             });
30362             waitTimer = Roo.TaskMgr.start({
30363                 run: function(i){
30364                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30365                 },
30366                 interval: 1000
30367             });
30368             return this;
30369         },
30370
30371         /**
30372          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30373          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30374          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30375          * @param {String} title The title bar text
30376          * @param {String} msg The message box body text
30377          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30378          * @param {Object} scope (optional) The scope of the callback function
30379          * @return {Roo.MessageBox} This message box
30380          */
30381         confirm : function(title, msg, fn, scope){
30382             this.show({
30383                 title : title,
30384                 msg : msg,
30385                 buttons: this.YESNO,
30386                 fn: fn,
30387                 scope : scope,
30388                 modal : true
30389             });
30390             return this;
30391         },
30392
30393         /**
30394          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30395          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30396          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30397          * (could also be the top-right close button) and the text that was entered will be passed as the two
30398          * parameters to the callback.
30399          * @param {String} title The title bar text
30400          * @param {String} msg The message box body text
30401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30402          * @param {Object} scope (optional) The scope of the callback function
30403          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30404          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30405          * @return {Roo.MessageBox} This message box
30406          */
30407         prompt : function(title, msg, fn, scope, multiline){
30408             this.show({
30409                 title : title,
30410                 msg : msg,
30411                 buttons: this.OKCANCEL,
30412                 fn: fn,
30413                 minWidth:250,
30414                 scope : scope,
30415                 prompt:true,
30416                 multiline: multiline,
30417                 modal : true
30418             });
30419             return this;
30420         },
30421
30422         /**
30423          * Button config that displays a single OK button
30424          * @type Object
30425          */
30426         OK : {ok:true},
30427         /**
30428          * Button config that displays Yes and No buttons
30429          * @type Object
30430          */
30431         YESNO : {yes:true, no:true},
30432         /**
30433          * Button config that displays OK and Cancel buttons
30434          * @type Object
30435          */
30436         OKCANCEL : {ok:true, cancel:true},
30437         /**
30438          * Button config that displays Yes, No and Cancel buttons
30439          * @type Object
30440          */
30441         YESNOCANCEL : {yes:true, no:true, cancel:true},
30442
30443         /**
30444          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30445          * @type Number
30446          */
30447         defaultTextHeight : 75,
30448         /**
30449          * The maximum width in pixels of the message box (defaults to 600)
30450          * @type Number
30451          */
30452         maxWidth : 600,
30453         /**
30454          * The minimum width in pixels of the message box (defaults to 100)
30455          * @type Number
30456          */
30457         minWidth : 100,
30458         /**
30459          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30460          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30461          * @type Number
30462          */
30463         minProgressWidth : 250,
30464         /**
30465          * An object containing the default button text strings that can be overriden for localized language support.
30466          * Supported properties are: ok, cancel, yes and no.
30467          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30468          * @type Object
30469          */
30470         buttonText : {
30471             ok : "OK",
30472             cancel : "Cancel",
30473             yes : "Yes",
30474             no : "No"
30475         }
30476     };
30477 }();
30478
30479 /**
30480  * Shorthand for {@link Roo.MessageBox}
30481  */
30482 Roo.Msg = Roo.MessageBox;/*
30483  * Based on:
30484  * Ext JS Library 1.1.1
30485  * Copyright(c) 2006-2007, Ext JS, LLC.
30486  *
30487  * Originally Released Under LGPL - original licence link has changed is not relivant.
30488  *
30489  * Fork - LGPL
30490  * <script type="text/javascript">
30491  */
30492 /**
30493  * @class Roo.QuickTips
30494  * Provides attractive and customizable tooltips for any element.
30495  * @singleton
30496  */
30497 Roo.QuickTips = function(){
30498     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30499     var ce, bd, xy, dd;
30500     var visible = false, disabled = true, inited = false;
30501     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30502     
30503     var onOver = function(e){
30504         if(disabled){
30505             return;
30506         }
30507         var t = e.getTarget();
30508         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30509             return;
30510         }
30511         if(ce && t == ce.el){
30512             clearTimeout(hideProc);
30513             return;
30514         }
30515         if(t && tagEls[t.id]){
30516             tagEls[t.id].el = t;
30517             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30518             return;
30519         }
30520         var ttp, et = Roo.fly(t);
30521         var ns = cfg.namespace;
30522         if(tm.interceptTitles && t.title){
30523             ttp = t.title;
30524             t.qtip = ttp;
30525             t.removeAttribute("title");
30526             e.preventDefault();
30527         }else{
30528             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30529         }
30530         if(ttp){
30531             showProc = show.defer(tm.showDelay, tm, [{
30532                 el: t, 
30533                 text: ttp, 
30534                 width: et.getAttributeNS(ns, cfg.width),
30535                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30536                 title: et.getAttributeNS(ns, cfg.title),
30537                     cls: et.getAttributeNS(ns, cfg.cls)
30538             }]);
30539         }
30540     };
30541     
30542     var onOut = function(e){
30543         clearTimeout(showProc);
30544         var t = e.getTarget();
30545         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30546             hideProc = setTimeout(hide, tm.hideDelay);
30547         }
30548     };
30549     
30550     var onMove = function(e){
30551         if(disabled){
30552             return;
30553         }
30554         xy = e.getXY();
30555         xy[1] += 18;
30556         if(tm.trackMouse && ce){
30557             el.setXY(xy);
30558         }
30559     };
30560     
30561     var onDown = function(e){
30562         clearTimeout(showProc);
30563         clearTimeout(hideProc);
30564         if(!e.within(el)){
30565             if(tm.hideOnClick){
30566                 hide();
30567                 tm.disable();
30568                 tm.enable.defer(100, tm);
30569             }
30570         }
30571     };
30572     
30573     var getPad = function(){
30574         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30575     };
30576
30577     var show = function(o){
30578         if(disabled){
30579             return;
30580         }
30581         clearTimeout(dismissProc);
30582         ce = o;
30583         if(removeCls){ // in case manually hidden
30584             el.removeClass(removeCls);
30585             removeCls = null;
30586         }
30587         if(ce.cls){
30588             el.addClass(ce.cls);
30589             removeCls = ce.cls;
30590         }
30591         if(ce.title){
30592             tipTitle.update(ce.title);
30593             tipTitle.show();
30594         }else{
30595             tipTitle.update('');
30596             tipTitle.hide();
30597         }
30598         el.dom.style.width  = tm.maxWidth+'px';
30599         //tipBody.dom.style.width = '';
30600         tipBodyText.update(o.text);
30601         var p = getPad(), w = ce.width;
30602         if(!w){
30603             var td = tipBodyText.dom;
30604             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30605             if(aw > tm.maxWidth){
30606                 w = tm.maxWidth;
30607             }else if(aw < tm.minWidth){
30608                 w = tm.minWidth;
30609             }else{
30610                 w = aw;
30611             }
30612         }
30613         //tipBody.setWidth(w);
30614         el.setWidth(parseInt(w, 10) + p);
30615         if(ce.autoHide === false){
30616             close.setDisplayed(true);
30617             if(dd){
30618                 dd.unlock();
30619             }
30620         }else{
30621             close.setDisplayed(false);
30622             if(dd){
30623                 dd.lock();
30624             }
30625         }
30626         if(xy){
30627             el.avoidY = xy[1]-18;
30628             el.setXY(xy);
30629         }
30630         if(tm.animate){
30631             el.setOpacity(.1);
30632             el.setStyle("visibility", "visible");
30633             el.fadeIn({callback: afterShow});
30634         }else{
30635             afterShow();
30636         }
30637     };
30638     
30639     var afterShow = function(){
30640         if(ce){
30641             el.show();
30642             esc.enable();
30643             if(tm.autoDismiss && ce.autoHide !== false){
30644                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30645             }
30646         }
30647     };
30648     
30649     var hide = function(noanim){
30650         clearTimeout(dismissProc);
30651         clearTimeout(hideProc);
30652         ce = null;
30653         if(el.isVisible()){
30654             esc.disable();
30655             if(noanim !== true && tm.animate){
30656                 el.fadeOut({callback: afterHide});
30657             }else{
30658                 afterHide();
30659             } 
30660         }
30661     };
30662     
30663     var afterHide = function(){
30664         el.hide();
30665         if(removeCls){
30666             el.removeClass(removeCls);
30667             removeCls = null;
30668         }
30669     };
30670     
30671     return {
30672         /**
30673         * @cfg {Number} minWidth
30674         * The minimum width of the quick tip (defaults to 40)
30675         */
30676        minWidth : 40,
30677         /**
30678         * @cfg {Number} maxWidth
30679         * The maximum width of the quick tip (defaults to 300)
30680         */
30681        maxWidth : 300,
30682         /**
30683         * @cfg {Boolean} interceptTitles
30684         * True to automatically use the element's DOM title value if available (defaults to false)
30685         */
30686        interceptTitles : false,
30687         /**
30688         * @cfg {Boolean} trackMouse
30689         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30690         */
30691        trackMouse : false,
30692         /**
30693         * @cfg {Boolean} hideOnClick
30694         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30695         */
30696        hideOnClick : true,
30697         /**
30698         * @cfg {Number} showDelay
30699         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30700         */
30701        showDelay : 500,
30702         /**
30703         * @cfg {Number} hideDelay
30704         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30705         */
30706        hideDelay : 200,
30707         /**
30708         * @cfg {Boolean} autoHide
30709         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30710         * Used in conjunction with hideDelay.
30711         */
30712        autoHide : true,
30713         /**
30714         * @cfg {Boolean}
30715         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30716         * (defaults to true).  Used in conjunction with autoDismissDelay.
30717         */
30718        autoDismiss : true,
30719         /**
30720         * @cfg {Number}
30721         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30722         */
30723        autoDismissDelay : 5000,
30724        /**
30725         * @cfg {Boolean} animate
30726         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30727         */
30728        animate : false,
30729
30730        /**
30731         * @cfg {String} title
30732         * Title text to display (defaults to '').  This can be any valid HTML markup.
30733         */
30734         title: '',
30735        /**
30736         * @cfg {String} text
30737         * Body text to display (defaults to '').  This can be any valid HTML markup.
30738         */
30739         text : '',
30740        /**
30741         * @cfg {String} cls
30742         * A CSS class to apply to the base quick tip element (defaults to '').
30743         */
30744         cls : '',
30745        /**
30746         * @cfg {Number} width
30747         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30748         * minWidth or maxWidth.
30749         */
30750         width : null,
30751
30752     /**
30753      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30754      * or display QuickTips in a page.
30755      */
30756        init : function(){
30757           tm = Roo.QuickTips;
30758           cfg = tm.tagConfig;
30759           if(!inited){
30760               if(!Roo.isReady){ // allow calling of init() before onReady
30761                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30762                   return;
30763               }
30764               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30765               el.fxDefaults = {stopFx: true};
30766               // maximum custom styling
30767               //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>');
30768               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>');              
30769               tipTitle = el.child('h3');
30770               tipTitle.enableDisplayMode("block");
30771               tipBody = el.child('div.x-tip-bd');
30772               tipBodyText = el.child('div.x-tip-bd-inner');
30773               //bdLeft = el.child('div.x-tip-bd-left');
30774               //bdRight = el.child('div.x-tip-bd-right');
30775               close = el.child('div.x-tip-close');
30776               close.enableDisplayMode("block");
30777               close.on("click", hide);
30778               var d = Roo.get(document);
30779               d.on("mousedown", onDown);
30780               d.on("mouseover", onOver);
30781               d.on("mouseout", onOut);
30782               d.on("mousemove", onMove);
30783               esc = d.addKeyListener(27, hide);
30784               esc.disable();
30785               if(Roo.dd.DD){
30786                   dd = el.initDD("default", null, {
30787                       onDrag : function(){
30788                           el.sync();  
30789                       }
30790                   });
30791                   dd.setHandleElId(tipTitle.id);
30792                   dd.lock();
30793               }
30794               inited = true;
30795           }
30796           this.enable(); 
30797        },
30798
30799     /**
30800      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30801      * are supported:
30802      * <pre>
30803 Property    Type                   Description
30804 ----------  ---------------------  ------------------------------------------------------------------------
30805 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30806      * </ul>
30807      * @param {Object} config The config object
30808      */
30809        register : function(config){
30810            var cs = config instanceof Array ? config : arguments;
30811            for(var i = 0, len = cs.length; i < len; i++) {
30812                var c = cs[i];
30813                var target = c.target;
30814                if(target){
30815                    if(target instanceof Array){
30816                        for(var j = 0, jlen = target.length; j < jlen; j++){
30817                            tagEls[target[j]] = c;
30818                        }
30819                    }else{
30820                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30821                    }
30822                }
30823            }
30824        },
30825
30826     /**
30827      * Removes this quick tip from its element and destroys it.
30828      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30829      */
30830        unregister : function(el){
30831            delete tagEls[Roo.id(el)];
30832        },
30833
30834     /**
30835      * Enable this quick tip.
30836      */
30837        enable : function(){
30838            if(inited && disabled){
30839                locks.pop();
30840                if(locks.length < 1){
30841                    disabled = false;
30842                }
30843            }
30844        },
30845
30846     /**
30847      * Disable this quick tip.
30848      */
30849        disable : function(){
30850           disabled = true;
30851           clearTimeout(showProc);
30852           clearTimeout(hideProc);
30853           clearTimeout(dismissProc);
30854           if(ce){
30855               hide(true);
30856           }
30857           locks.push(1);
30858        },
30859
30860     /**
30861      * Returns true if the quick tip is enabled, else false.
30862      */
30863        isEnabled : function(){
30864             return !disabled;
30865        },
30866
30867         // private
30868        tagConfig : {
30869            namespace : "ext",
30870            attribute : "qtip",
30871            width : "width",
30872            target : "target",
30873            title : "qtitle",
30874            hide : "hide",
30875            cls : "qclass"
30876        }
30877    };
30878 }();
30879
30880 // backwards compat
30881 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30882  * Based on:
30883  * Ext JS Library 1.1.1
30884  * Copyright(c) 2006-2007, Ext JS, LLC.
30885  *
30886  * Originally Released Under LGPL - original licence link has changed is not relivant.
30887  *
30888  * Fork - LGPL
30889  * <script type="text/javascript">
30890  */
30891  
30892
30893 /**
30894  * @class Roo.tree.TreePanel
30895  * @extends Roo.data.Tree
30896
30897  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30898  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30899  * @cfg {Boolean} enableDD true to enable drag and drop
30900  * @cfg {Boolean} enableDrag true to enable just drag
30901  * @cfg {Boolean} enableDrop true to enable just drop
30902  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30903  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30904  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30905  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30906  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30907  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30908  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30909  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30910  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30911  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30912  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30913  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30914  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30915  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30916  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / 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>
30917  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / 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>
30918  * 
30919  * @constructor
30920  * @param {String/HTMLElement/Element} el The container element
30921  * @param {Object} config
30922  */
30923 Roo.tree.TreePanel = function(el, config){
30924     var root = false;
30925     var loader = false;
30926     if (config.root) {
30927         root = config.root;
30928         delete config.root;
30929     }
30930     if (config.loader) {
30931         loader = config.loader;
30932         delete config.loader;
30933     }
30934     
30935     Roo.apply(this, config);
30936     Roo.tree.TreePanel.superclass.constructor.call(this);
30937     this.el = Roo.get(el);
30938     this.el.addClass('x-tree');
30939     //console.log(root);
30940     if (root) {
30941         this.setRootNode( Roo.factory(root, Roo.tree));
30942     }
30943     if (loader) {
30944         this.loader = Roo.factory(loader, Roo.tree);
30945     }
30946    /**
30947     * Read-only. The id of the container element becomes this TreePanel's id.
30948     */
30949     this.id = this.el.id;
30950     this.addEvents({
30951         /**
30952         * @event beforeload
30953         * Fires before a node is loaded, return false to cancel
30954         * @param {Node} node The node being loaded
30955         */
30956         "beforeload" : true,
30957         /**
30958         * @event load
30959         * Fires when a node is loaded
30960         * @param {Node} node The node that was loaded
30961         */
30962         "load" : true,
30963         /**
30964         * @event textchange
30965         * Fires when the text for a node is changed
30966         * @param {Node} node The node
30967         * @param {String} text The new text
30968         * @param {String} oldText The old text
30969         */
30970         "textchange" : true,
30971         /**
30972         * @event beforeexpand
30973         * Fires before a node is expanded, return false to cancel.
30974         * @param {Node} node The node
30975         * @param {Boolean} deep
30976         * @param {Boolean} anim
30977         */
30978         "beforeexpand" : true,
30979         /**
30980         * @event beforecollapse
30981         * Fires before a node is collapsed, return false to cancel.
30982         * @param {Node} node The node
30983         * @param {Boolean} deep
30984         * @param {Boolean} anim
30985         */
30986         "beforecollapse" : true,
30987         /**
30988         * @event expand
30989         * Fires when a node is expanded
30990         * @param {Node} node The node
30991         */
30992         "expand" : true,
30993         /**
30994         * @event disabledchange
30995         * Fires when the disabled status of a node changes
30996         * @param {Node} node The node
30997         * @param {Boolean} disabled
30998         */
30999         "disabledchange" : true,
31000         /**
31001         * @event collapse
31002         * Fires when a node is collapsed
31003         * @param {Node} node The node
31004         */
31005         "collapse" : true,
31006         /**
31007         * @event beforeclick
31008         * Fires before click processing on a node. Return false to cancel the default action.
31009         * @param {Node} node The node
31010         * @param {Roo.EventObject} e The event object
31011         */
31012         "beforeclick":true,
31013         /**
31014         * @event checkchange
31015         * Fires when a node with a checkbox's checked property changes
31016         * @param {Node} this This node
31017         * @param {Boolean} checked
31018         */
31019         "checkchange":true,
31020         /**
31021         * @event click
31022         * Fires when a node is clicked
31023         * @param {Node} node The node
31024         * @param {Roo.EventObject} e The event object
31025         */
31026         "click":true,
31027         /**
31028         * @event dblclick
31029         * Fires when a node is double clicked
31030         * @param {Node} node The node
31031         * @param {Roo.EventObject} e The event object
31032         */
31033         "dblclick":true,
31034         /**
31035         * @event contextmenu
31036         * Fires when a node is right clicked
31037         * @param {Node} node The node
31038         * @param {Roo.EventObject} e The event object
31039         */
31040         "contextmenu":true,
31041         /**
31042         * @event beforechildrenrendered
31043         * Fires right before the child nodes for a node are rendered
31044         * @param {Node} node The node
31045         */
31046         "beforechildrenrendered":true,
31047         /**
31048         * @event startdrag
31049         * Fires when a node starts being dragged
31050         * @param {Roo.tree.TreePanel} this
31051         * @param {Roo.tree.TreeNode} node
31052         * @param {event} e The raw browser event
31053         */ 
31054        "startdrag" : true,
31055        /**
31056         * @event enddrag
31057         * Fires when a drag operation is complete
31058         * @param {Roo.tree.TreePanel} this
31059         * @param {Roo.tree.TreeNode} node
31060         * @param {event} e The raw browser event
31061         */
31062        "enddrag" : true,
31063        /**
31064         * @event dragdrop
31065         * Fires when a dragged node is dropped on a valid DD target
31066         * @param {Roo.tree.TreePanel} this
31067         * @param {Roo.tree.TreeNode} node
31068         * @param {DD} dd The dd it was dropped on
31069         * @param {event} e The raw browser event
31070         */
31071        "dragdrop" : true,
31072        /**
31073         * @event beforenodedrop
31074         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31075         * passed to handlers has the following properties:<br />
31076         * <ul style="padding:5px;padding-left:16px;">
31077         * <li>tree - The TreePanel</li>
31078         * <li>target - The node being targeted for the drop</li>
31079         * <li>data - The drag data from the drag source</li>
31080         * <li>point - The point of the drop - append, above or below</li>
31081         * <li>source - The drag source</li>
31082         * <li>rawEvent - Raw mouse event</li>
31083         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31084         * to be inserted by setting them on this object.</li>
31085         * <li>cancel - Set this to true to cancel the drop.</li>
31086         * </ul>
31087         * @param {Object} dropEvent
31088         */
31089        "beforenodedrop" : true,
31090        /**
31091         * @event nodedrop
31092         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31093         * passed to handlers has the following properties:<br />
31094         * <ul style="padding:5px;padding-left:16px;">
31095         * <li>tree - The TreePanel</li>
31096         * <li>target - The node being targeted for the drop</li>
31097         * <li>data - The drag data from the drag source</li>
31098         * <li>point - The point of the drop - append, above or below</li>
31099         * <li>source - The drag source</li>
31100         * <li>rawEvent - Raw mouse event</li>
31101         * <li>dropNode - Dropped node(s).</li>
31102         * </ul>
31103         * @param {Object} dropEvent
31104         */
31105        "nodedrop" : true,
31106         /**
31107         * @event nodedragover
31108         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31109         * passed to handlers has the following properties:<br />
31110         * <ul style="padding:5px;padding-left:16px;">
31111         * <li>tree - The TreePanel</li>
31112         * <li>target - The node being targeted for the drop</li>
31113         * <li>data - The drag data from the drag source</li>
31114         * <li>point - The point of the drop - append, above or below</li>
31115         * <li>source - The drag source</li>
31116         * <li>rawEvent - Raw mouse event</li>
31117         * <li>dropNode - Drop node(s) provided by the source.</li>
31118         * <li>cancel - Set this to true to signal drop not allowed.</li>
31119         * </ul>
31120         * @param {Object} dragOverEvent
31121         */
31122        "nodedragover" : true
31123         
31124     });
31125     if(this.singleExpand){
31126        this.on("beforeexpand", this.restrictExpand, this);
31127     }
31128     if (this.editor) {
31129         this.editor.tree = this;
31130         this.editor = Roo.factory(this.editor, Roo.tree);
31131     }
31132     
31133     if (this.selModel) {
31134         this.selModel = Roo.factory(this.selModel, Roo.tree);
31135     }
31136    
31137 };
31138 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31139     rootVisible : true,
31140     animate: Roo.enableFx,
31141     lines : true,
31142     enableDD : false,
31143     hlDrop : Roo.enableFx,
31144   
31145     renderer: false,
31146     
31147     rendererTip: false,
31148     // private
31149     restrictExpand : function(node){
31150         var p = node.parentNode;
31151         if(p){
31152             if(p.expandedChild && p.expandedChild.parentNode == p){
31153                 p.expandedChild.collapse();
31154             }
31155             p.expandedChild = node;
31156         }
31157     },
31158
31159     // private override
31160     setRootNode : function(node){
31161         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31162         if(!this.rootVisible){
31163             node.ui = new Roo.tree.RootTreeNodeUI(node);
31164         }
31165         return node;
31166     },
31167
31168     /**
31169      * Returns the container element for this TreePanel
31170      */
31171     getEl : function(){
31172         return this.el;
31173     },
31174
31175     /**
31176      * Returns the default TreeLoader for this TreePanel
31177      */
31178     getLoader : function(){
31179         return this.loader;
31180     },
31181
31182     /**
31183      * Expand all nodes
31184      */
31185     expandAll : function(){
31186         this.root.expand(true);
31187     },
31188
31189     /**
31190      * Collapse all nodes
31191      */
31192     collapseAll : function(){
31193         this.root.collapse(true);
31194     },
31195
31196     /**
31197      * Returns the selection model used by this TreePanel
31198      */
31199     getSelectionModel : function(){
31200         if(!this.selModel){
31201             this.selModel = new Roo.tree.DefaultSelectionModel();
31202         }
31203         return this.selModel;
31204     },
31205
31206     /**
31207      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31208      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31209      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31210      * @return {Array}
31211      */
31212     getChecked : function(a, startNode){
31213         startNode = startNode || this.root;
31214         var r = [];
31215         var f = function(){
31216             if(this.attributes.checked){
31217                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31218             }
31219         }
31220         startNode.cascade(f);
31221         return r;
31222     },
31223
31224     /**
31225      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31226      * @param {String} path
31227      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31228      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31229      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31230      */
31231     expandPath : function(path, attr, callback){
31232         attr = attr || "id";
31233         var keys = path.split(this.pathSeparator);
31234         var curNode = this.root;
31235         if(curNode.attributes[attr] != keys[1]){ // invalid root
31236             if(callback){
31237                 callback(false, null);
31238             }
31239             return;
31240         }
31241         var index = 1;
31242         var f = function(){
31243             if(++index == keys.length){
31244                 if(callback){
31245                     callback(true, curNode);
31246                 }
31247                 return;
31248             }
31249             var c = curNode.findChild(attr, keys[index]);
31250             if(!c){
31251                 if(callback){
31252                     callback(false, curNode);
31253                 }
31254                 return;
31255             }
31256             curNode = c;
31257             c.expand(false, false, f);
31258         };
31259         curNode.expand(false, false, f);
31260     },
31261
31262     /**
31263      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31264      * @param {String} path
31265      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31266      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31267      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31268      */
31269     selectPath : function(path, attr, callback){
31270         attr = attr || "id";
31271         var keys = path.split(this.pathSeparator);
31272         var v = keys.pop();
31273         if(keys.length > 0){
31274             var f = function(success, node){
31275                 if(success && node){
31276                     var n = node.findChild(attr, v);
31277                     if(n){
31278                         n.select();
31279                         if(callback){
31280                             callback(true, n);
31281                         }
31282                     }else if(callback){
31283                         callback(false, n);
31284                     }
31285                 }else{
31286                     if(callback){
31287                         callback(false, n);
31288                     }
31289                 }
31290             };
31291             this.expandPath(keys.join(this.pathSeparator), attr, f);
31292         }else{
31293             this.root.select();
31294             if(callback){
31295                 callback(true, this.root);
31296             }
31297         }
31298     },
31299
31300     getTreeEl : function(){
31301         return this.el;
31302     },
31303
31304     /**
31305      * Trigger rendering of this TreePanel
31306      */
31307     render : function(){
31308         if (this.innerCt) {
31309             return this; // stop it rendering more than once!!
31310         }
31311         
31312         this.innerCt = this.el.createChild({tag:"ul",
31313                cls:"x-tree-root-ct " +
31314                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31315
31316         if(this.containerScroll){
31317             Roo.dd.ScrollManager.register(this.el);
31318         }
31319         if((this.enableDD || this.enableDrop) && !this.dropZone){
31320            /**
31321             * The dropZone used by this tree if drop is enabled
31322             * @type Roo.tree.TreeDropZone
31323             */
31324              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31325                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31326            });
31327         }
31328         if((this.enableDD || this.enableDrag) && !this.dragZone){
31329            /**
31330             * The dragZone used by this tree if drag is enabled
31331             * @type Roo.tree.TreeDragZone
31332             */
31333             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31334                ddGroup: this.ddGroup || "TreeDD",
31335                scroll: this.ddScroll
31336            });
31337         }
31338         this.getSelectionModel().init(this);
31339         if (!this.root) {
31340             console.log("ROOT not set in tree");
31341             return;
31342         }
31343         this.root.render();
31344         if(!this.rootVisible){
31345             this.root.renderChildren();
31346         }
31347         return this;
31348     }
31349 });/*
31350  * Based on:
31351  * Ext JS Library 1.1.1
31352  * Copyright(c) 2006-2007, Ext JS, LLC.
31353  *
31354  * Originally Released Under LGPL - original licence link has changed is not relivant.
31355  *
31356  * Fork - LGPL
31357  * <script type="text/javascript">
31358  */
31359  
31360
31361 /**
31362  * @class Roo.tree.DefaultSelectionModel
31363  * @extends Roo.util.Observable
31364  * The default single selection for a TreePanel.
31365  * @param {Object} cfg Configuration
31366  */
31367 Roo.tree.DefaultSelectionModel = function(cfg){
31368    this.selNode = null;
31369    
31370    
31371    
31372    this.addEvents({
31373        /**
31374         * @event selectionchange
31375         * Fires when the selected node changes
31376         * @param {DefaultSelectionModel} this
31377         * @param {TreeNode} node the new selection
31378         */
31379        "selectionchange" : true,
31380
31381        /**
31382         * @event beforeselect
31383         * Fires before the selected node changes, return false to cancel the change
31384         * @param {DefaultSelectionModel} this
31385         * @param {TreeNode} node the new selection
31386         * @param {TreeNode} node the old selection
31387         */
31388        "beforeselect" : true
31389    });
31390    
31391     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31392 };
31393
31394 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31395     init : function(tree){
31396         this.tree = tree;
31397         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31398         tree.on("click", this.onNodeClick, this);
31399     },
31400     
31401     onNodeClick : function(node, e){
31402         if (e.ctrlKey && this.selNode == node)  {
31403             this.unselect(node);
31404             return;
31405         }
31406         this.select(node);
31407     },
31408     
31409     /**
31410      * Select a node.
31411      * @param {TreeNode} node The node to select
31412      * @return {TreeNode} The selected node
31413      */
31414     select : function(node){
31415         var last = this.selNode;
31416         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31417             if(last){
31418                 last.ui.onSelectedChange(false);
31419             }
31420             this.selNode = node;
31421             node.ui.onSelectedChange(true);
31422             this.fireEvent("selectionchange", this, node, last);
31423         }
31424         return node;
31425     },
31426     
31427     /**
31428      * Deselect a node.
31429      * @param {TreeNode} node The node to unselect
31430      */
31431     unselect : function(node){
31432         if(this.selNode == node){
31433             this.clearSelections();
31434         }    
31435     },
31436     
31437     /**
31438      * Clear all selections
31439      */
31440     clearSelections : function(){
31441         var n = this.selNode;
31442         if(n){
31443             n.ui.onSelectedChange(false);
31444             this.selNode = null;
31445             this.fireEvent("selectionchange", this, null);
31446         }
31447         return n;
31448     },
31449     
31450     /**
31451      * Get the selected node
31452      * @return {TreeNode} The selected node
31453      */
31454     getSelectedNode : function(){
31455         return this.selNode;    
31456     },
31457     
31458     /**
31459      * Returns true if the node is selected
31460      * @param {TreeNode} node The node to check
31461      * @return {Boolean}
31462      */
31463     isSelected : function(node){
31464         return this.selNode == node;  
31465     },
31466
31467     /**
31468      * Selects the node above the selected node in the tree, intelligently walking the nodes
31469      * @return TreeNode The new selection
31470      */
31471     selectPrevious : function(){
31472         var s = this.selNode || this.lastSelNode;
31473         if(!s){
31474             return null;
31475         }
31476         var ps = s.previousSibling;
31477         if(ps){
31478             if(!ps.isExpanded() || ps.childNodes.length < 1){
31479                 return this.select(ps);
31480             } else{
31481                 var lc = ps.lastChild;
31482                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31483                     lc = lc.lastChild;
31484                 }
31485                 return this.select(lc);
31486             }
31487         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31488             return this.select(s.parentNode);
31489         }
31490         return null;
31491     },
31492
31493     /**
31494      * Selects the node above the selected node in the tree, intelligently walking the nodes
31495      * @return TreeNode The new selection
31496      */
31497     selectNext : function(){
31498         var s = this.selNode || this.lastSelNode;
31499         if(!s){
31500             return null;
31501         }
31502         if(s.firstChild && s.isExpanded()){
31503              return this.select(s.firstChild);
31504          }else if(s.nextSibling){
31505              return this.select(s.nextSibling);
31506          }else if(s.parentNode){
31507             var newS = null;
31508             s.parentNode.bubble(function(){
31509                 if(this.nextSibling){
31510                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31511                     return false;
31512                 }
31513             });
31514             return newS;
31515          }
31516         return null;
31517     },
31518
31519     onKeyDown : function(e){
31520         var s = this.selNode || this.lastSelNode;
31521         // undesirable, but required
31522         var sm = this;
31523         if(!s){
31524             return;
31525         }
31526         var k = e.getKey();
31527         switch(k){
31528              case e.DOWN:
31529                  e.stopEvent();
31530                  this.selectNext();
31531              break;
31532              case e.UP:
31533                  e.stopEvent();
31534                  this.selectPrevious();
31535              break;
31536              case e.RIGHT:
31537                  e.preventDefault();
31538                  if(s.hasChildNodes()){
31539                      if(!s.isExpanded()){
31540                          s.expand();
31541                      }else if(s.firstChild){
31542                          this.select(s.firstChild, e);
31543                      }
31544                  }
31545              break;
31546              case e.LEFT:
31547                  e.preventDefault();
31548                  if(s.hasChildNodes() && s.isExpanded()){
31549                      s.collapse();
31550                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31551                      this.select(s.parentNode, e);
31552                  }
31553              break;
31554         };
31555     }
31556 });
31557
31558 /**
31559  * @class Roo.tree.MultiSelectionModel
31560  * @extends Roo.util.Observable
31561  * Multi selection for a TreePanel.
31562  * @param {Object} cfg Configuration
31563  */
31564 Roo.tree.MultiSelectionModel = function(){
31565    this.selNodes = [];
31566    this.selMap = {};
31567    this.addEvents({
31568        /**
31569         * @event selectionchange
31570         * Fires when the selected nodes change
31571         * @param {MultiSelectionModel} this
31572         * @param {Array} nodes Array of the selected nodes
31573         */
31574        "selectionchange" : true
31575    });
31576    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31577    
31578 };
31579
31580 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31581     init : function(tree){
31582         this.tree = tree;
31583         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31584         tree.on("click", this.onNodeClick, this);
31585     },
31586     
31587     onNodeClick : function(node, e){
31588         this.select(node, e, e.ctrlKey);
31589     },
31590     
31591     /**
31592      * Select a node.
31593      * @param {TreeNode} node The node to select
31594      * @param {EventObject} e (optional) An event associated with the selection
31595      * @param {Boolean} keepExisting True to retain existing selections
31596      * @return {TreeNode} The selected node
31597      */
31598     select : function(node, e, keepExisting){
31599         if(keepExisting !== true){
31600             this.clearSelections(true);
31601         }
31602         if(this.isSelected(node)){
31603             this.lastSelNode = node;
31604             return node;
31605         }
31606         this.selNodes.push(node);
31607         this.selMap[node.id] = node;
31608         this.lastSelNode = node;
31609         node.ui.onSelectedChange(true);
31610         this.fireEvent("selectionchange", this, this.selNodes);
31611         return node;
31612     },
31613     
31614     /**
31615      * Deselect a node.
31616      * @param {TreeNode} node The node to unselect
31617      */
31618     unselect : function(node){
31619         if(this.selMap[node.id]){
31620             node.ui.onSelectedChange(false);
31621             var sn = this.selNodes;
31622             var index = -1;
31623             if(sn.indexOf){
31624                 index = sn.indexOf(node);
31625             }else{
31626                 for(var i = 0, len = sn.length; i < len; i++){
31627                     if(sn[i] == node){
31628                         index = i;
31629                         break;
31630                     }
31631                 }
31632             }
31633             if(index != -1){
31634                 this.selNodes.splice(index, 1);
31635             }
31636             delete this.selMap[node.id];
31637             this.fireEvent("selectionchange", this, this.selNodes);
31638         }
31639     },
31640     
31641     /**
31642      * Clear all selections
31643      */
31644     clearSelections : function(suppressEvent){
31645         var sn = this.selNodes;
31646         if(sn.length > 0){
31647             for(var i = 0, len = sn.length; i < len; i++){
31648                 sn[i].ui.onSelectedChange(false);
31649             }
31650             this.selNodes = [];
31651             this.selMap = {};
31652             if(suppressEvent !== true){
31653                 this.fireEvent("selectionchange", this, this.selNodes);
31654             }
31655         }
31656     },
31657     
31658     /**
31659      * Returns true if the node is selected
31660      * @param {TreeNode} node The node to check
31661      * @return {Boolean}
31662      */
31663     isSelected : function(node){
31664         return this.selMap[node.id] ? true : false;  
31665     },
31666     
31667     /**
31668      * Returns an array of the selected nodes
31669      * @return {Array}
31670      */
31671     getSelectedNodes : function(){
31672         return this.selNodes;    
31673     },
31674
31675     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31676
31677     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31678
31679     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31680 });/*
31681  * Based on:
31682  * Ext JS Library 1.1.1
31683  * Copyright(c) 2006-2007, Ext JS, LLC.
31684  *
31685  * Originally Released Under LGPL - original licence link has changed is not relivant.
31686  *
31687  * Fork - LGPL
31688  * <script type="text/javascript">
31689  */
31690  
31691 /**
31692  * @class Roo.tree.TreeNode
31693  * @extends Roo.data.Node
31694  * @cfg {String} text The text for this node
31695  * @cfg {Boolean} expanded true to start the node expanded
31696  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31697  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31698  * @cfg {Boolean} disabled true to start the node disabled
31699  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31700  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31701  * @cfg {String} cls A css class to be added to the node
31702  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31703  * @cfg {String} href URL of the link used for the node (defaults to #)
31704  * @cfg {String} hrefTarget target frame for the link
31705  * @cfg {String} qtip An Ext QuickTip for the node
31706  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31707  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31708  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31709  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31710  * (defaults to undefined with no checkbox rendered)
31711  * @constructor
31712  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31713  */
31714 Roo.tree.TreeNode = function(attributes){
31715     attributes = attributes || {};
31716     if(typeof attributes == "string"){
31717         attributes = {text: attributes};
31718     }
31719     this.childrenRendered = false;
31720     this.rendered = false;
31721     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31722     this.expanded = attributes.expanded === true;
31723     this.isTarget = attributes.isTarget !== false;
31724     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31725     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31726
31727     /**
31728      * Read-only. The text for this node. To change it use setText().
31729      * @type String
31730      */
31731     this.text = attributes.text;
31732     /**
31733      * True if this node is disabled.
31734      * @type Boolean
31735      */
31736     this.disabled = attributes.disabled === true;
31737
31738     this.addEvents({
31739         /**
31740         * @event textchange
31741         * Fires when the text for this node is changed
31742         * @param {Node} this This node
31743         * @param {String} text The new text
31744         * @param {String} oldText The old text
31745         */
31746         "textchange" : true,
31747         /**
31748         * @event beforeexpand
31749         * Fires before this node is expanded, return false to cancel.
31750         * @param {Node} this This node
31751         * @param {Boolean} deep
31752         * @param {Boolean} anim
31753         */
31754         "beforeexpand" : true,
31755         /**
31756         * @event beforecollapse
31757         * Fires before this node is collapsed, return false to cancel.
31758         * @param {Node} this This node
31759         * @param {Boolean} deep
31760         * @param {Boolean} anim
31761         */
31762         "beforecollapse" : true,
31763         /**
31764         * @event expand
31765         * Fires when this node is expanded
31766         * @param {Node} this This node
31767         */
31768         "expand" : true,
31769         /**
31770         * @event disabledchange
31771         * Fires when the disabled status of this node changes
31772         * @param {Node} this This node
31773         * @param {Boolean} disabled
31774         */
31775         "disabledchange" : true,
31776         /**
31777         * @event collapse
31778         * Fires when this node is collapsed
31779         * @param {Node} this This node
31780         */
31781         "collapse" : true,
31782         /**
31783         * @event beforeclick
31784         * Fires before click processing. Return false to cancel the default action.
31785         * @param {Node} this This node
31786         * @param {Roo.EventObject} e The event object
31787         */
31788         "beforeclick":true,
31789         /**
31790         * @event checkchange
31791         * Fires when a node with a checkbox's checked property changes
31792         * @param {Node} this This node
31793         * @param {Boolean} checked
31794         */
31795         "checkchange":true,
31796         /**
31797         * @event click
31798         * Fires when this node is clicked
31799         * @param {Node} this This node
31800         * @param {Roo.EventObject} e The event object
31801         */
31802         "click":true,
31803         /**
31804         * @event dblclick
31805         * Fires when this node is double clicked
31806         * @param {Node} this This node
31807         * @param {Roo.EventObject} e The event object
31808         */
31809         "dblclick":true,
31810         /**
31811         * @event contextmenu
31812         * Fires when this node is right clicked
31813         * @param {Node} this This node
31814         * @param {Roo.EventObject} e The event object
31815         */
31816         "contextmenu":true,
31817         /**
31818         * @event beforechildrenrendered
31819         * Fires right before the child nodes for this node are rendered
31820         * @param {Node} this This node
31821         */
31822         "beforechildrenrendered":true
31823     });
31824
31825     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31826
31827     /**
31828      * Read-only. The UI for this node
31829      * @type TreeNodeUI
31830      */
31831     this.ui = new uiClass(this);
31832 };
31833 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31834     preventHScroll: true,
31835     /**
31836      * Returns true if this node is expanded
31837      * @return {Boolean}
31838      */
31839     isExpanded : function(){
31840         return this.expanded;
31841     },
31842
31843     /**
31844      * Returns the UI object for this node
31845      * @return {TreeNodeUI}
31846      */
31847     getUI : function(){
31848         return this.ui;
31849     },
31850
31851     // private override
31852     setFirstChild : function(node){
31853         var of = this.firstChild;
31854         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31855         if(this.childrenRendered && of && node != of){
31856             of.renderIndent(true, true);
31857         }
31858         if(this.rendered){
31859             this.renderIndent(true, true);
31860         }
31861     },
31862
31863     // private override
31864     setLastChild : function(node){
31865         var ol = this.lastChild;
31866         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31867         if(this.childrenRendered && ol && node != ol){
31868             ol.renderIndent(true, true);
31869         }
31870         if(this.rendered){
31871             this.renderIndent(true, true);
31872         }
31873     },
31874
31875     // these methods are overridden to provide lazy rendering support
31876     // private override
31877     appendChild : function(){
31878         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31879         if(node && this.childrenRendered){
31880             node.render();
31881         }
31882         this.ui.updateExpandIcon();
31883         return node;
31884     },
31885
31886     // private override
31887     removeChild : function(node){
31888         this.ownerTree.getSelectionModel().unselect(node);
31889         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31890         // if it's been rendered remove dom node
31891         if(this.childrenRendered){
31892             node.ui.remove();
31893         }
31894         if(this.childNodes.length < 1){
31895             this.collapse(false, false);
31896         }else{
31897             this.ui.updateExpandIcon();
31898         }
31899         if(!this.firstChild) {
31900             this.childrenRendered = false;
31901         }
31902         return node;
31903     },
31904
31905     // private override
31906     insertBefore : function(node, refNode){
31907         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31908         if(newNode && refNode && this.childrenRendered){
31909             node.render();
31910         }
31911         this.ui.updateExpandIcon();
31912         return newNode;
31913     },
31914
31915     /**
31916      * Sets the text for this node
31917      * @param {String} text
31918      */
31919     setText : function(text){
31920         var oldText = this.text;
31921         this.text = text;
31922         this.attributes.text = text;
31923         if(this.rendered){ // event without subscribing
31924             this.ui.onTextChange(this, text, oldText);
31925         }
31926         this.fireEvent("textchange", this, text, oldText);
31927     },
31928
31929     /**
31930      * Triggers selection of this node
31931      */
31932     select : function(){
31933         this.getOwnerTree().getSelectionModel().select(this);
31934     },
31935
31936     /**
31937      * Triggers deselection of this node
31938      */
31939     unselect : function(){
31940         this.getOwnerTree().getSelectionModel().unselect(this);
31941     },
31942
31943     /**
31944      * Returns true if this node is selected
31945      * @return {Boolean}
31946      */
31947     isSelected : function(){
31948         return this.getOwnerTree().getSelectionModel().isSelected(this);
31949     },
31950
31951     /**
31952      * Expand this node.
31953      * @param {Boolean} deep (optional) True to expand all children as well
31954      * @param {Boolean} anim (optional) false to cancel the default animation
31955      * @param {Function} callback (optional) A callback to be called when
31956      * expanding this node completes (does not wait for deep expand to complete).
31957      * Called with 1 parameter, this node.
31958      */
31959     expand : function(deep, anim, callback){
31960         if(!this.expanded){
31961             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31962                 return;
31963             }
31964             if(!this.childrenRendered){
31965                 this.renderChildren();
31966             }
31967             this.expanded = true;
31968             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31969                 this.ui.animExpand(function(){
31970                     this.fireEvent("expand", this);
31971                     if(typeof callback == "function"){
31972                         callback(this);
31973                     }
31974                     if(deep === true){
31975                         this.expandChildNodes(true);
31976                     }
31977                 }.createDelegate(this));
31978                 return;
31979             }else{
31980                 this.ui.expand();
31981                 this.fireEvent("expand", this);
31982                 if(typeof callback == "function"){
31983                     callback(this);
31984                 }
31985             }
31986         }else{
31987            if(typeof callback == "function"){
31988                callback(this);
31989            }
31990         }
31991         if(deep === true){
31992             this.expandChildNodes(true);
31993         }
31994     },
31995
31996     isHiddenRoot : function(){
31997         return this.isRoot && !this.getOwnerTree().rootVisible;
31998     },
31999
32000     /**
32001      * Collapse this node.
32002      * @param {Boolean} deep (optional) True to collapse all children as well
32003      * @param {Boolean} anim (optional) false to cancel the default animation
32004      */
32005     collapse : function(deep, anim){
32006         if(this.expanded && !this.isHiddenRoot()){
32007             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32008                 return;
32009             }
32010             this.expanded = false;
32011             if((this.getOwnerTree().animate && anim !== false) || anim){
32012                 this.ui.animCollapse(function(){
32013                     this.fireEvent("collapse", this);
32014                     if(deep === true){
32015                         this.collapseChildNodes(true);
32016                     }
32017                 }.createDelegate(this));
32018                 return;
32019             }else{
32020                 this.ui.collapse();
32021                 this.fireEvent("collapse", this);
32022             }
32023         }
32024         if(deep === true){
32025             var cs = this.childNodes;
32026             for(var i = 0, len = cs.length; i < len; i++) {
32027                 cs[i].collapse(true, false);
32028             }
32029         }
32030     },
32031
32032     // private
32033     delayedExpand : function(delay){
32034         if(!this.expandProcId){
32035             this.expandProcId = this.expand.defer(delay, this);
32036         }
32037     },
32038
32039     // private
32040     cancelExpand : function(){
32041         if(this.expandProcId){
32042             clearTimeout(this.expandProcId);
32043         }
32044         this.expandProcId = false;
32045     },
32046
32047     /**
32048      * Toggles expanded/collapsed state of the node
32049      */
32050     toggle : function(){
32051         if(this.expanded){
32052             this.collapse();
32053         }else{
32054             this.expand();
32055         }
32056     },
32057
32058     /**
32059      * Ensures all parent nodes are expanded
32060      */
32061     ensureVisible : function(callback){
32062         var tree = this.getOwnerTree();
32063         tree.expandPath(this.parentNode.getPath(), false, function(){
32064             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32065             Roo.callback(callback);
32066         }.createDelegate(this));
32067     },
32068
32069     /**
32070      * Expand all child nodes
32071      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32072      */
32073     expandChildNodes : function(deep){
32074         var cs = this.childNodes;
32075         for(var i = 0, len = cs.length; i < len; i++) {
32076                 cs[i].expand(deep);
32077         }
32078     },
32079
32080     /**
32081      * Collapse all child nodes
32082      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32083      */
32084     collapseChildNodes : function(deep){
32085         var cs = this.childNodes;
32086         for(var i = 0, len = cs.length; i < len; i++) {
32087                 cs[i].collapse(deep);
32088         }
32089     },
32090
32091     /**
32092      * Disables this node
32093      */
32094     disable : function(){
32095         this.disabled = true;
32096         this.unselect();
32097         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32098             this.ui.onDisableChange(this, true);
32099         }
32100         this.fireEvent("disabledchange", this, true);
32101     },
32102
32103     /**
32104      * Enables this node
32105      */
32106     enable : function(){
32107         this.disabled = false;
32108         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32109             this.ui.onDisableChange(this, false);
32110         }
32111         this.fireEvent("disabledchange", this, false);
32112     },
32113
32114     // private
32115     renderChildren : function(suppressEvent){
32116         if(suppressEvent !== false){
32117             this.fireEvent("beforechildrenrendered", this);
32118         }
32119         var cs = this.childNodes;
32120         for(var i = 0, len = cs.length; i < len; i++){
32121             cs[i].render(true);
32122         }
32123         this.childrenRendered = true;
32124     },
32125
32126     // private
32127     sort : function(fn, scope){
32128         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32129         if(this.childrenRendered){
32130             var cs = this.childNodes;
32131             for(var i = 0, len = cs.length; i < len; i++){
32132                 cs[i].render(true);
32133             }
32134         }
32135     },
32136
32137     // private
32138     render : function(bulkRender){
32139         this.ui.render(bulkRender);
32140         if(!this.rendered){
32141             this.rendered = true;
32142             if(this.expanded){
32143                 this.expanded = false;
32144                 this.expand(false, false);
32145             }
32146         }
32147     },
32148
32149     // private
32150     renderIndent : function(deep, refresh){
32151         if(refresh){
32152             this.ui.childIndent = null;
32153         }
32154         this.ui.renderIndent();
32155         if(deep === true && this.childrenRendered){
32156             var cs = this.childNodes;
32157             for(var i = 0, len = cs.length; i < len; i++){
32158                 cs[i].renderIndent(true, refresh);
32159             }
32160         }
32161     }
32162 });/*
32163  * Based on:
32164  * Ext JS Library 1.1.1
32165  * Copyright(c) 2006-2007, Ext JS, LLC.
32166  *
32167  * Originally Released Under LGPL - original licence link has changed is not relivant.
32168  *
32169  * Fork - LGPL
32170  * <script type="text/javascript">
32171  */
32172  
32173 /**
32174  * @class Roo.tree.AsyncTreeNode
32175  * @extends Roo.tree.TreeNode
32176  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32177  * @constructor
32178  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32179  */
32180  Roo.tree.AsyncTreeNode = function(config){
32181     this.loaded = false;
32182     this.loading = false;
32183     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32184     /**
32185     * @event beforeload
32186     * Fires before this node is loaded, return false to cancel
32187     * @param {Node} this This node
32188     */
32189     this.addEvents({'beforeload':true, 'load': true});
32190     /**
32191     * @event load
32192     * Fires when this node is loaded
32193     * @param {Node} this This node
32194     */
32195     /**
32196      * The loader used by this node (defaults to using the tree's defined loader)
32197      * @type TreeLoader
32198      * @property loader
32199      */
32200 };
32201 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32202     expand : function(deep, anim, callback){
32203         if(this.loading){ // if an async load is already running, waiting til it's done
32204             var timer;
32205             var f = function(){
32206                 if(!this.loading){ // done loading
32207                     clearInterval(timer);
32208                     this.expand(deep, anim, callback);
32209                 }
32210             }.createDelegate(this);
32211             timer = setInterval(f, 200);
32212             return;
32213         }
32214         if(!this.loaded){
32215             if(this.fireEvent("beforeload", this) === false){
32216                 return;
32217             }
32218             this.loading = true;
32219             this.ui.beforeLoad(this);
32220             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32221             if(loader){
32222                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32223                 return;
32224             }
32225         }
32226         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32227     },
32228     
32229     /**
32230      * Returns true if this node is currently loading
32231      * @return {Boolean}
32232      */
32233     isLoading : function(){
32234         return this.loading;  
32235     },
32236     
32237     loadComplete : function(deep, anim, callback){
32238         this.loading = false;
32239         this.loaded = true;
32240         this.ui.afterLoad(this);
32241         this.fireEvent("load", this);
32242         this.expand(deep, anim, callback);
32243     },
32244     
32245     /**
32246      * Returns true if this node has been loaded
32247      * @return {Boolean}
32248      */
32249     isLoaded : function(){
32250         return this.loaded;
32251     },
32252     
32253     hasChildNodes : function(){
32254         if(!this.isLeaf() && !this.loaded){
32255             return true;
32256         }else{
32257             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32258         }
32259     },
32260
32261     /**
32262      * Trigger a reload for this node
32263      * @param {Function} callback
32264      */
32265     reload : function(callback){
32266         this.collapse(false, false);
32267         while(this.firstChild){
32268             this.removeChild(this.firstChild);
32269         }
32270         this.childrenRendered = false;
32271         this.loaded = false;
32272         if(this.isHiddenRoot()){
32273             this.expanded = false;
32274         }
32275         this.expand(false, false, callback);
32276     }
32277 });/*
32278  * Based on:
32279  * Ext JS Library 1.1.1
32280  * Copyright(c) 2006-2007, Ext JS, LLC.
32281  *
32282  * Originally Released Under LGPL - original licence link has changed is not relivant.
32283  *
32284  * Fork - LGPL
32285  * <script type="text/javascript">
32286  */
32287  
32288 /**
32289  * @class Roo.tree.TreeNodeUI
32290  * @constructor
32291  * @param {Object} node The node to render
32292  * The TreeNode UI implementation is separate from the
32293  * tree implementation. Unless you are customizing the tree UI,
32294  * you should never have to use this directly.
32295  */
32296 Roo.tree.TreeNodeUI = function(node){
32297     this.node = node;
32298     this.rendered = false;
32299     this.animating = false;
32300     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32301 };
32302
32303 Roo.tree.TreeNodeUI.prototype = {
32304     removeChild : function(node){
32305         if(this.rendered){
32306             this.ctNode.removeChild(node.ui.getEl());
32307         }
32308     },
32309
32310     beforeLoad : function(){
32311          this.addClass("x-tree-node-loading");
32312     },
32313
32314     afterLoad : function(){
32315          this.removeClass("x-tree-node-loading");
32316     },
32317
32318     onTextChange : function(node, text, oldText){
32319         if(this.rendered){
32320             this.textNode.innerHTML = text;
32321         }
32322     },
32323
32324     onDisableChange : function(node, state){
32325         this.disabled = state;
32326         if(state){
32327             this.addClass("x-tree-node-disabled");
32328         }else{
32329             this.removeClass("x-tree-node-disabled");
32330         }
32331     },
32332
32333     onSelectedChange : function(state){
32334         if(state){
32335             this.focus();
32336             this.addClass("x-tree-selected");
32337         }else{
32338             //this.blur();
32339             this.removeClass("x-tree-selected");
32340         }
32341     },
32342
32343     onMove : function(tree, node, oldParent, newParent, index, refNode){
32344         this.childIndent = null;
32345         if(this.rendered){
32346             var targetNode = newParent.ui.getContainer();
32347             if(!targetNode){//target not rendered
32348                 this.holder = document.createElement("div");
32349                 this.holder.appendChild(this.wrap);
32350                 return;
32351             }
32352             var insertBefore = refNode ? refNode.ui.getEl() : null;
32353             if(insertBefore){
32354                 targetNode.insertBefore(this.wrap, insertBefore);
32355             }else{
32356                 targetNode.appendChild(this.wrap);
32357             }
32358             this.node.renderIndent(true);
32359         }
32360     },
32361
32362     addClass : function(cls){
32363         if(this.elNode){
32364             Roo.fly(this.elNode).addClass(cls);
32365         }
32366     },
32367
32368     removeClass : function(cls){
32369         if(this.elNode){
32370             Roo.fly(this.elNode).removeClass(cls);
32371         }
32372     },
32373
32374     remove : function(){
32375         if(this.rendered){
32376             this.holder = document.createElement("div");
32377             this.holder.appendChild(this.wrap);
32378         }
32379     },
32380
32381     fireEvent : function(){
32382         return this.node.fireEvent.apply(this.node, arguments);
32383     },
32384
32385     initEvents : function(){
32386         this.node.on("move", this.onMove, this);
32387         var E = Roo.EventManager;
32388         var a = this.anchor;
32389
32390         var el = Roo.fly(a, '_treeui');
32391
32392         if(Roo.isOpera){ // opera render bug ignores the CSS
32393             el.setStyle("text-decoration", "none");
32394         }
32395
32396         el.on("click", this.onClick, this);
32397         el.on("dblclick", this.onDblClick, this);
32398
32399         if(this.checkbox){
32400             Roo.EventManager.on(this.checkbox,
32401                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32402         }
32403
32404         el.on("contextmenu", this.onContextMenu, this);
32405
32406         var icon = Roo.fly(this.iconNode);
32407         icon.on("click", this.onClick, this);
32408         icon.on("dblclick", this.onDblClick, this);
32409         icon.on("contextmenu", this.onContextMenu, this);
32410         E.on(this.ecNode, "click", this.ecClick, this, true);
32411
32412         if(this.node.disabled){
32413             this.addClass("x-tree-node-disabled");
32414         }
32415         if(this.node.hidden){
32416             this.addClass("x-tree-node-disabled");
32417         }
32418         var ot = this.node.getOwnerTree();
32419         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32420         if(dd && (!this.node.isRoot || ot.rootVisible)){
32421             Roo.dd.Registry.register(this.elNode, {
32422                 node: this.node,
32423                 handles: this.getDDHandles(),
32424                 isHandle: false
32425             });
32426         }
32427     },
32428
32429     getDDHandles : function(){
32430         return [this.iconNode, this.textNode];
32431     },
32432
32433     hide : function(){
32434         if(this.rendered){
32435             this.wrap.style.display = "none";
32436         }
32437     },
32438
32439     show : function(){
32440         if(this.rendered){
32441             this.wrap.style.display = "";
32442         }
32443     },
32444
32445     onContextMenu : function(e){
32446         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32447             e.preventDefault();
32448             this.focus();
32449             this.fireEvent("contextmenu", this.node, e);
32450         }
32451     },
32452
32453     onClick : function(e){
32454         if(this.dropping){
32455             e.stopEvent();
32456             return;
32457         }
32458         if(this.fireEvent("beforeclick", this.node, e) !== false){
32459             if(!this.disabled && this.node.attributes.href){
32460                 this.fireEvent("click", this.node, e);
32461                 return;
32462             }
32463             e.preventDefault();
32464             if(this.disabled){
32465                 return;
32466             }
32467
32468             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32469                 this.node.toggle();
32470             }
32471
32472             this.fireEvent("click", this.node, e);
32473         }else{
32474             e.stopEvent();
32475         }
32476     },
32477
32478     onDblClick : function(e){
32479         e.preventDefault();
32480         if(this.disabled){
32481             return;
32482         }
32483         if(this.checkbox){
32484             this.toggleCheck();
32485         }
32486         if(!this.animating && this.node.hasChildNodes()){
32487             this.node.toggle();
32488         }
32489         this.fireEvent("dblclick", this.node, e);
32490     },
32491
32492     onCheckChange : function(){
32493         var checked = this.checkbox.checked;
32494         this.node.attributes.checked = checked;
32495         this.fireEvent('checkchange', this.node, checked);
32496     },
32497
32498     ecClick : function(e){
32499         if(!this.animating && this.node.hasChildNodes()){
32500             this.node.toggle();
32501         }
32502     },
32503
32504     startDrop : function(){
32505         this.dropping = true;
32506     },
32507
32508     // delayed drop so the click event doesn't get fired on a drop
32509     endDrop : function(){
32510        setTimeout(function(){
32511            this.dropping = false;
32512        }.createDelegate(this), 50);
32513     },
32514
32515     expand : function(){
32516         this.updateExpandIcon();
32517         this.ctNode.style.display = "";
32518     },
32519
32520     focus : function(){
32521         if(!this.node.preventHScroll){
32522             try{this.anchor.focus();
32523             }catch(e){}
32524         }else if(!Roo.isIE){
32525             try{
32526                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32527                 var l = noscroll.scrollLeft;
32528                 this.anchor.focus();
32529                 noscroll.scrollLeft = l;
32530             }catch(e){}
32531         }
32532     },
32533
32534     toggleCheck : function(value){
32535         var cb = this.checkbox;
32536         if(cb){
32537             cb.checked = (value === undefined ? !cb.checked : value);
32538         }
32539     },
32540
32541     blur : function(){
32542         try{
32543             this.anchor.blur();
32544         }catch(e){}
32545     },
32546
32547     animExpand : function(callback){
32548         var ct = Roo.get(this.ctNode);
32549         ct.stopFx();
32550         if(!this.node.hasChildNodes()){
32551             this.updateExpandIcon();
32552             this.ctNode.style.display = "";
32553             Roo.callback(callback);
32554             return;
32555         }
32556         this.animating = true;
32557         this.updateExpandIcon();
32558
32559         ct.slideIn('t', {
32560            callback : function(){
32561                this.animating = false;
32562                Roo.callback(callback);
32563             },
32564             scope: this,
32565             duration: this.node.ownerTree.duration || .25
32566         });
32567     },
32568
32569     highlight : function(){
32570         var tree = this.node.getOwnerTree();
32571         Roo.fly(this.wrap).highlight(
32572             tree.hlColor || "C3DAF9",
32573             {endColor: tree.hlBaseColor}
32574         );
32575     },
32576
32577     collapse : function(){
32578         this.updateExpandIcon();
32579         this.ctNode.style.display = "none";
32580     },
32581
32582     animCollapse : function(callback){
32583         var ct = Roo.get(this.ctNode);
32584         ct.enableDisplayMode('block');
32585         ct.stopFx();
32586
32587         this.animating = true;
32588         this.updateExpandIcon();
32589
32590         ct.slideOut('t', {
32591             callback : function(){
32592                this.animating = false;
32593                Roo.callback(callback);
32594             },
32595             scope: this,
32596             duration: this.node.ownerTree.duration || .25
32597         });
32598     },
32599
32600     getContainer : function(){
32601         return this.ctNode;
32602     },
32603
32604     getEl : function(){
32605         return this.wrap;
32606     },
32607
32608     appendDDGhost : function(ghostNode){
32609         ghostNode.appendChild(this.elNode.cloneNode(true));
32610     },
32611
32612     getDDRepairXY : function(){
32613         return Roo.lib.Dom.getXY(this.iconNode);
32614     },
32615
32616     onRender : function(){
32617         this.render();
32618     },
32619
32620     render : function(bulkRender){
32621         var n = this.node, a = n.attributes;
32622         var targetNode = n.parentNode ?
32623               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32624
32625         if(!this.rendered){
32626             this.rendered = true;
32627
32628             this.renderElements(n, a, targetNode, bulkRender);
32629
32630             if(a.qtip){
32631                if(this.textNode.setAttributeNS){
32632                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32633                    if(a.qtipTitle){
32634                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32635                    }
32636                }else{
32637                    this.textNode.setAttribute("ext:qtip", a.qtip);
32638                    if(a.qtipTitle){
32639                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32640                    }
32641                }
32642             }else if(a.qtipCfg){
32643                 a.qtipCfg.target = Roo.id(this.textNode);
32644                 Roo.QuickTips.register(a.qtipCfg);
32645             }
32646             this.initEvents();
32647             if(!this.node.expanded){
32648                 this.updateExpandIcon();
32649             }
32650         }else{
32651             if(bulkRender === true) {
32652                 targetNode.appendChild(this.wrap);
32653             }
32654         }
32655     },
32656
32657     renderElements : function(n, a, targetNode, bulkRender)
32658     {
32659         // add some indent caching, this helps performance when rendering a large tree
32660         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32661         var t = n.getOwnerTree();
32662         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32663         if (typeof(n.attributes.html) != 'undefined') {
32664             txt = n.attributes.html;
32665         }
32666         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32667         var cb = typeof a.checked == 'boolean';
32668         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32669         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32670             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32671             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32672             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32673             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32674             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32675              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32676                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32677             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32678             "</li>"];
32679
32680         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32681             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32682                                 n.nextSibling.ui.getEl(), buf.join(""));
32683         }else{
32684             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32685         }
32686
32687         this.elNode = this.wrap.childNodes[0];
32688         this.ctNode = this.wrap.childNodes[1];
32689         var cs = this.elNode.childNodes;
32690         this.indentNode = cs[0];
32691         this.ecNode = cs[1];
32692         this.iconNode = cs[2];
32693         var index = 3;
32694         if(cb){
32695             this.checkbox = cs[3];
32696             index++;
32697         }
32698         this.anchor = cs[index];
32699         this.textNode = cs[index].firstChild;
32700     },
32701
32702     getAnchor : function(){
32703         return this.anchor;
32704     },
32705
32706     getTextEl : function(){
32707         return this.textNode;
32708     },
32709
32710     getIconEl : function(){
32711         return this.iconNode;
32712     },
32713
32714     isChecked : function(){
32715         return this.checkbox ? this.checkbox.checked : false;
32716     },
32717
32718     updateExpandIcon : function(){
32719         if(this.rendered){
32720             var n = this.node, c1, c2;
32721             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32722             var hasChild = n.hasChildNodes();
32723             if(hasChild){
32724                 if(n.expanded){
32725                     cls += "-minus";
32726                     c1 = "x-tree-node-collapsed";
32727                     c2 = "x-tree-node-expanded";
32728                 }else{
32729                     cls += "-plus";
32730                     c1 = "x-tree-node-expanded";
32731                     c2 = "x-tree-node-collapsed";
32732                 }
32733                 if(this.wasLeaf){
32734                     this.removeClass("x-tree-node-leaf");
32735                     this.wasLeaf = false;
32736                 }
32737                 if(this.c1 != c1 || this.c2 != c2){
32738                     Roo.fly(this.elNode).replaceClass(c1, c2);
32739                     this.c1 = c1; this.c2 = c2;
32740                 }
32741             }else{
32742                 // this changes non-leafs into leafs if they have no children.
32743                 // it's not very rational behaviour..
32744                 
32745                 if(!this.wasLeaf && this.node.leaf){
32746                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32747                     delete this.c1;
32748                     delete this.c2;
32749                     this.wasLeaf = true;
32750                 }
32751             }
32752             var ecc = "x-tree-ec-icon "+cls;
32753             if(this.ecc != ecc){
32754                 this.ecNode.className = ecc;
32755                 this.ecc = ecc;
32756             }
32757         }
32758     },
32759
32760     getChildIndent : function(){
32761         if(!this.childIndent){
32762             var buf = [];
32763             var p = this.node;
32764             while(p){
32765                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32766                     if(!p.isLast()) {
32767                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32768                     } else {
32769                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32770                     }
32771                 }
32772                 p = p.parentNode;
32773             }
32774             this.childIndent = buf.join("");
32775         }
32776         return this.childIndent;
32777     },
32778
32779     renderIndent : function(){
32780         if(this.rendered){
32781             var indent = "";
32782             var p = this.node.parentNode;
32783             if(p){
32784                 indent = p.ui.getChildIndent();
32785             }
32786             if(this.indentMarkup != indent){ // don't rerender if not required
32787                 this.indentNode.innerHTML = indent;
32788                 this.indentMarkup = indent;
32789             }
32790             this.updateExpandIcon();
32791         }
32792     }
32793 };
32794
32795 Roo.tree.RootTreeNodeUI = function(){
32796     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32797 };
32798 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32799     render : function(){
32800         if(!this.rendered){
32801             var targetNode = this.node.ownerTree.innerCt.dom;
32802             this.node.expanded = true;
32803             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32804             this.wrap = this.ctNode = targetNode.firstChild;
32805         }
32806     },
32807     collapse : function(){
32808     },
32809     expand : function(){
32810     }
32811 });/*
32812  * Based on:
32813  * Ext JS Library 1.1.1
32814  * Copyright(c) 2006-2007, Ext JS, LLC.
32815  *
32816  * Originally Released Under LGPL - original licence link has changed is not relivant.
32817  *
32818  * Fork - LGPL
32819  * <script type="text/javascript">
32820  */
32821 /**
32822  * @class Roo.tree.TreeLoader
32823  * @extends Roo.util.Observable
32824  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32825  * nodes from a specified URL. The response must be a javascript Array definition
32826  * who's elements are node definition objects. eg:
32827  * <pre><code>
32828    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32829     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32830 </code></pre>
32831  * <br><br>
32832  * A server request is sent, and child nodes are loaded only when a node is expanded.
32833  * The loading node's id is passed to the server under the parameter name "node" to
32834  * enable the server to produce the correct child nodes.
32835  * <br><br>
32836  * To pass extra parameters, an event handler may be attached to the "beforeload"
32837  * event, and the parameters specified in the TreeLoader's baseParams property:
32838  * <pre><code>
32839     myTreeLoader.on("beforeload", function(treeLoader, node) {
32840         this.baseParams.category = node.attributes.category;
32841     }, this);
32842 </code></pre><
32843  * This would pass an HTTP parameter called "category" to the server containing
32844  * the value of the Node's "category" attribute.
32845  * @constructor
32846  * Creates a new Treeloader.
32847  * @param {Object} config A config object containing config properties.
32848  */
32849 Roo.tree.TreeLoader = function(config){
32850     this.baseParams = {};
32851     this.requestMethod = "POST";
32852     Roo.apply(this, config);
32853
32854     this.addEvents({
32855     
32856         /**
32857          * @event beforeload
32858          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32859          * @param {Object} This TreeLoader object.
32860          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32861          * @param {Object} callback The callback function specified in the {@link #load} call.
32862          */
32863         beforeload : true,
32864         /**
32865          * @event load
32866          * Fires when the node has been successfuly loaded.
32867          * @param {Object} This TreeLoader object.
32868          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32869          * @param {Object} response The response object containing the data from the server.
32870          */
32871         load : true,
32872         /**
32873          * @event loadexception
32874          * Fires if the network request failed.
32875          * @param {Object} This TreeLoader object.
32876          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32877          * @param {Object} response The response object containing the data from the server.
32878          */
32879         loadexception : true,
32880         /**
32881          * @event create
32882          * Fires before a node is created, enabling you to return custom Node types 
32883          * @param {Object} This TreeLoader object.
32884          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32885          */
32886         create : true
32887     });
32888
32889     Roo.tree.TreeLoader.superclass.constructor.call(this);
32890 };
32891
32892 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32893     /**
32894     * @cfg {String} dataUrl The URL from which to request a Json string which
32895     * specifies an array of node definition object representing the child nodes
32896     * to be loaded.
32897     */
32898     /**
32899     * @cfg {Object} baseParams (optional) An object containing properties which
32900     * specify HTTP parameters to be passed to each request for child nodes.
32901     */
32902     /**
32903     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32904     * created by this loader. If the attributes sent by the server have an attribute in this object,
32905     * they take priority.
32906     */
32907     /**
32908     * @cfg {Object} uiProviders (optional) An object containing properties which
32909     * 
32910     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32911     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32912     * <i>uiProvider</i> attribute of a returned child node is a string rather
32913     * than a reference to a TreeNodeUI implementation, this that string value
32914     * is used as a property name in the uiProviders object. You can define the provider named
32915     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32916     */
32917     uiProviders : {},
32918
32919     /**
32920     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32921     * child nodes before loading.
32922     */
32923     clearOnLoad : true,
32924
32925     /**
32926     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32927     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32928     * Grid query { data : [ .....] }
32929     */
32930     
32931     root : false,
32932      /**
32933     * @cfg {String} queryParam (optional) 
32934     * Name of the query as it will be passed on the querystring (defaults to 'node')
32935     * eg. the request will be ?node=[id]
32936     */
32937     
32938     
32939     queryParam: false,
32940     
32941     /**
32942      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32943      * This is called automatically when a node is expanded, but may be used to reload
32944      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32945      * @param {Roo.tree.TreeNode} node
32946      * @param {Function} callback
32947      */
32948     load : function(node, callback){
32949         if(this.clearOnLoad){
32950             while(node.firstChild){
32951                 node.removeChild(node.firstChild);
32952             }
32953         }
32954         if(node.attributes.children){ // preloaded json children
32955             var cs = node.attributes.children;
32956             for(var i = 0, len = cs.length; i < len; i++){
32957                 node.appendChild(this.createNode(cs[i]));
32958             }
32959             if(typeof callback == "function"){
32960                 callback();
32961             }
32962         }else if(this.dataUrl){
32963             this.requestData(node, callback);
32964         }
32965     },
32966
32967     getParams: function(node){
32968         var buf = [], bp = this.baseParams;
32969         for(var key in bp){
32970             if(typeof bp[key] != "function"){
32971                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32972             }
32973         }
32974         var n = this.queryParam === false ? 'node' : this.queryParam;
32975         buf.push(n + "=", encodeURIComponent(node.id));
32976         return buf.join("");
32977     },
32978
32979     requestData : function(node, callback){
32980         if(this.fireEvent("beforeload", this, node, callback) !== false){
32981             this.transId = Roo.Ajax.request({
32982                 method:this.requestMethod,
32983                 url: this.dataUrl||this.url,
32984                 success: this.handleResponse,
32985                 failure: this.handleFailure,
32986                 scope: this,
32987                 argument: {callback: callback, node: node},
32988                 params: this.getParams(node)
32989             });
32990         }else{
32991             // if the load is cancelled, make sure we notify
32992             // the node that we are done
32993             if(typeof callback == "function"){
32994                 callback();
32995             }
32996         }
32997     },
32998
32999     isLoading : function(){
33000         return this.transId ? true : false;
33001     },
33002
33003     abort : function(){
33004         if(this.isLoading()){
33005             Roo.Ajax.abort(this.transId);
33006         }
33007     },
33008
33009     // private
33010     createNode : function(attr)
33011     {
33012         // apply baseAttrs, nice idea Corey!
33013         if(this.baseAttrs){
33014             Roo.applyIf(attr, this.baseAttrs);
33015         }
33016         if(this.applyLoader !== false){
33017             attr.loader = this;
33018         }
33019         // uiProvider = depreciated..
33020         
33021         if(typeof(attr.uiProvider) == 'string'){
33022            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33023                 /**  eval:var:attr */ eval(attr.uiProvider);
33024         }
33025         if(typeof(this.uiProviders['default']) != 'undefined') {
33026             attr.uiProvider = this.uiProviders['default'];
33027         }
33028         
33029         this.fireEvent('create', this, attr);
33030         
33031         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33032         return(attr.leaf ?
33033                         new Roo.tree.TreeNode(attr) :
33034                         new Roo.tree.AsyncTreeNode(attr));
33035     },
33036
33037     processResponse : function(response, node, callback)
33038     {
33039         var json = response.responseText;
33040         try {
33041             
33042             var o = Roo.decode(json);
33043             
33044             if (!o.success) {
33045                 // it's a failure condition.
33046                 var a = response.argument;
33047                 this.fireEvent("loadexception", this, a.node, response);
33048                 Roo.log("Load failed - should have a handler really");
33049                 return;
33050             }
33051             
33052             if (this.root !== false) {
33053                 o = o[this.root];
33054             }
33055             
33056             for(var i = 0, len = o.length; i < len; i++){
33057                 var n = this.createNode(o[i]);
33058                 if(n){
33059                     node.appendChild(n);
33060                 }
33061             }
33062             if(typeof callback == "function"){
33063                 callback(this, node);
33064             }
33065         }catch(e){
33066             this.handleFailure(response);
33067         }
33068     },
33069
33070     handleResponse : function(response){
33071         this.transId = false;
33072         var a = response.argument;
33073         this.processResponse(response, a.node, a.callback);
33074         this.fireEvent("load", this, a.node, response);
33075     },
33076
33077     handleFailure : function(response)
33078     {
33079         // should handle failure better..
33080         this.transId = false;
33081         var a = response.argument;
33082         this.fireEvent("loadexception", this, a.node, response);
33083         if(typeof a.callback == "function"){
33084             a.callback(this, a.node);
33085         }
33086     }
33087 });/*
33088  * Based on:
33089  * Ext JS Library 1.1.1
33090  * Copyright(c) 2006-2007, Ext JS, LLC.
33091  *
33092  * Originally Released Under LGPL - original licence link has changed is not relivant.
33093  *
33094  * Fork - LGPL
33095  * <script type="text/javascript">
33096  */
33097
33098 /**
33099 * @class Roo.tree.TreeFilter
33100 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33101 * @param {TreePanel} tree
33102 * @param {Object} config (optional)
33103  */
33104 Roo.tree.TreeFilter = function(tree, config){
33105     this.tree = tree;
33106     this.filtered = {};
33107     Roo.apply(this, config);
33108 };
33109
33110 Roo.tree.TreeFilter.prototype = {
33111     clearBlank:false,
33112     reverse:false,
33113     autoClear:false,
33114     remove:false,
33115
33116      /**
33117      * Filter the data by a specific attribute.
33118      * @param {String/RegExp} value Either string that the attribute value
33119      * should start with or a RegExp to test against the attribute
33120      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33121      * @param {TreeNode} startNode (optional) The node to start the filter at.
33122      */
33123     filter : function(value, attr, startNode){
33124         attr = attr || "text";
33125         var f;
33126         if(typeof value == "string"){
33127             var vlen = value.length;
33128             // auto clear empty filter
33129             if(vlen == 0 && this.clearBlank){
33130                 this.clear();
33131                 return;
33132             }
33133             value = value.toLowerCase();
33134             f = function(n){
33135                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33136             };
33137         }else if(value.exec){ // regex?
33138             f = function(n){
33139                 return value.test(n.attributes[attr]);
33140             };
33141         }else{
33142             throw 'Illegal filter type, must be string or regex';
33143         }
33144         this.filterBy(f, null, startNode);
33145         },
33146
33147     /**
33148      * Filter by a function. The passed function will be called with each
33149      * node in the tree (or from the startNode). If the function returns true, the node is kept
33150      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33151      * @param {Function} fn The filter function
33152      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33153      */
33154     filterBy : function(fn, scope, startNode){
33155         startNode = startNode || this.tree.root;
33156         if(this.autoClear){
33157             this.clear();
33158         }
33159         var af = this.filtered, rv = this.reverse;
33160         var f = function(n){
33161             if(n == startNode){
33162                 return true;
33163             }
33164             if(af[n.id]){
33165                 return false;
33166             }
33167             var m = fn.call(scope || n, n);
33168             if(!m || rv){
33169                 af[n.id] = n;
33170                 n.ui.hide();
33171                 return false;
33172             }
33173             return true;
33174         };
33175         startNode.cascade(f);
33176         if(this.remove){
33177            for(var id in af){
33178                if(typeof id != "function"){
33179                    var n = af[id];
33180                    if(n && n.parentNode){
33181                        n.parentNode.removeChild(n);
33182                    }
33183                }
33184            }
33185         }
33186     },
33187
33188     /**
33189      * Clears the current filter. Note: with the "remove" option
33190      * set a filter cannot be cleared.
33191      */
33192     clear : function(){
33193         var t = this.tree;
33194         var af = this.filtered;
33195         for(var id in af){
33196             if(typeof id != "function"){
33197                 var n = af[id];
33198                 if(n){
33199                     n.ui.show();
33200                 }
33201             }
33202         }
33203         this.filtered = {};
33204     }
33205 };
33206 /*
33207  * Based on:
33208  * Ext JS Library 1.1.1
33209  * Copyright(c) 2006-2007, Ext JS, LLC.
33210  *
33211  * Originally Released Under LGPL - original licence link has changed is not relivant.
33212  *
33213  * Fork - LGPL
33214  * <script type="text/javascript">
33215  */
33216  
33217
33218 /**
33219  * @class Roo.tree.TreeSorter
33220  * Provides sorting of nodes in a TreePanel
33221  * 
33222  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33223  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33224  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33225  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33226  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33227  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33228  * @constructor
33229  * @param {TreePanel} tree
33230  * @param {Object} config
33231  */
33232 Roo.tree.TreeSorter = function(tree, config){
33233     Roo.apply(this, config);
33234     tree.on("beforechildrenrendered", this.doSort, this);
33235     tree.on("append", this.updateSort, this);
33236     tree.on("insert", this.updateSort, this);
33237     
33238     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33239     var p = this.property || "text";
33240     var sortType = this.sortType;
33241     var fs = this.folderSort;
33242     var cs = this.caseSensitive === true;
33243     var leafAttr = this.leafAttr || 'leaf';
33244
33245     this.sortFn = function(n1, n2){
33246         if(fs){
33247             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33248                 return 1;
33249             }
33250             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33251                 return -1;
33252             }
33253         }
33254         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33255         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33256         if(v1 < v2){
33257                         return dsc ? +1 : -1;
33258                 }else if(v1 > v2){
33259                         return dsc ? -1 : +1;
33260         }else{
33261                 return 0;
33262         }
33263     };
33264 };
33265
33266 Roo.tree.TreeSorter.prototype = {
33267     doSort : function(node){
33268         node.sort(this.sortFn);
33269     },
33270     
33271     compareNodes : function(n1, n2){
33272         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33273     },
33274     
33275     updateSort : function(tree, node){
33276         if(node.childrenRendered){
33277             this.doSort.defer(1, this, [node]);
33278         }
33279     }
33280 };/*
33281  * Based on:
33282  * Ext JS Library 1.1.1
33283  * Copyright(c) 2006-2007, Ext JS, LLC.
33284  *
33285  * Originally Released Under LGPL - original licence link has changed is not relivant.
33286  *
33287  * Fork - LGPL
33288  * <script type="text/javascript">
33289  */
33290
33291 if(Roo.dd.DropZone){
33292     
33293 Roo.tree.TreeDropZone = function(tree, config){
33294     this.allowParentInsert = false;
33295     this.allowContainerDrop = false;
33296     this.appendOnly = false;
33297     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33298     this.tree = tree;
33299     this.lastInsertClass = "x-tree-no-status";
33300     this.dragOverData = {};
33301 };
33302
33303 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33304     ddGroup : "TreeDD",
33305     
33306     expandDelay : 1000,
33307     
33308     expandNode : function(node){
33309         if(node.hasChildNodes() && !node.isExpanded()){
33310             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33311         }
33312     },
33313     
33314     queueExpand : function(node){
33315         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33316     },
33317     
33318     cancelExpand : function(){
33319         if(this.expandProcId){
33320             clearTimeout(this.expandProcId);
33321             this.expandProcId = false;
33322         }
33323     },
33324     
33325     isValidDropPoint : function(n, pt, dd, e, data){
33326         if(!n || !data){ return false; }
33327         var targetNode = n.node;
33328         var dropNode = data.node;
33329         // default drop rules
33330         if(!(targetNode && targetNode.isTarget && pt)){
33331             return false;
33332         }
33333         if(pt == "append" && targetNode.allowChildren === false){
33334             return false;
33335         }
33336         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33337             return false;
33338         }
33339         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33340             return false;
33341         }
33342         // reuse the object
33343         var overEvent = this.dragOverData;
33344         overEvent.tree = this.tree;
33345         overEvent.target = targetNode;
33346         overEvent.data = data;
33347         overEvent.point = pt;
33348         overEvent.source = dd;
33349         overEvent.rawEvent = e;
33350         overEvent.dropNode = dropNode;
33351         overEvent.cancel = false;  
33352         var result = this.tree.fireEvent("nodedragover", overEvent);
33353         return overEvent.cancel === false && result !== false;
33354     },
33355     
33356     getDropPoint : function(e, n, dd){
33357         var tn = n.node;
33358         if(tn.isRoot){
33359             return tn.allowChildren !== false ? "append" : false; // always append for root
33360         }
33361         var dragEl = n.ddel;
33362         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33363         var y = Roo.lib.Event.getPageY(e);
33364         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33365         
33366         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33367         var noAppend = tn.allowChildren === false;
33368         if(this.appendOnly || tn.parentNode.allowChildren === false){
33369             return noAppend ? false : "append";
33370         }
33371         var noBelow = false;
33372         if(!this.allowParentInsert){
33373             noBelow = tn.hasChildNodes() && tn.isExpanded();
33374         }
33375         var q = (b - t) / (noAppend ? 2 : 3);
33376         if(y >= t && y < (t + q)){
33377             return "above";
33378         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33379             return "below";
33380         }else{
33381             return "append";
33382         }
33383     },
33384     
33385     onNodeEnter : function(n, dd, e, data){
33386         this.cancelExpand();
33387     },
33388     
33389     onNodeOver : function(n, dd, e, data){
33390         var pt = this.getDropPoint(e, n, dd);
33391         var node = n.node;
33392         
33393         // auto node expand check
33394         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33395             this.queueExpand(node);
33396         }else if(pt != "append"){
33397             this.cancelExpand();
33398         }
33399         
33400         // set the insert point style on the target node
33401         var returnCls = this.dropNotAllowed;
33402         if(this.isValidDropPoint(n, pt, dd, e, data)){
33403            if(pt){
33404                var el = n.ddel;
33405                var cls;
33406                if(pt == "above"){
33407                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33408                    cls = "x-tree-drag-insert-above";
33409                }else if(pt == "below"){
33410                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33411                    cls = "x-tree-drag-insert-below";
33412                }else{
33413                    returnCls = "x-tree-drop-ok-append";
33414                    cls = "x-tree-drag-append";
33415                }
33416                if(this.lastInsertClass != cls){
33417                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33418                    this.lastInsertClass = cls;
33419                }
33420            }
33421        }
33422        return returnCls;
33423     },
33424     
33425     onNodeOut : function(n, dd, e, data){
33426         this.cancelExpand();
33427         this.removeDropIndicators(n);
33428     },
33429     
33430     onNodeDrop : function(n, dd, e, data){
33431         var point = this.getDropPoint(e, n, dd);
33432         var targetNode = n.node;
33433         targetNode.ui.startDrop();
33434         if(!this.isValidDropPoint(n, point, dd, e, data)){
33435             targetNode.ui.endDrop();
33436             return false;
33437         }
33438         // first try to find the drop node
33439         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33440         var dropEvent = {
33441             tree : this.tree,
33442             target: targetNode,
33443             data: data,
33444             point: point,
33445             source: dd,
33446             rawEvent: e,
33447             dropNode: dropNode,
33448             cancel: !dropNode   
33449         };
33450         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33451         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33452             targetNode.ui.endDrop();
33453             return false;
33454         }
33455         // allow target changing
33456         targetNode = dropEvent.target;
33457         if(point == "append" && !targetNode.isExpanded()){
33458             targetNode.expand(false, null, function(){
33459                 this.completeDrop(dropEvent);
33460             }.createDelegate(this));
33461         }else{
33462             this.completeDrop(dropEvent);
33463         }
33464         return true;
33465     },
33466     
33467     completeDrop : function(de){
33468         var ns = de.dropNode, p = de.point, t = de.target;
33469         if(!(ns instanceof Array)){
33470             ns = [ns];
33471         }
33472         var n;
33473         for(var i = 0, len = ns.length; i < len; i++){
33474             n = ns[i];
33475             if(p == "above"){
33476                 t.parentNode.insertBefore(n, t);
33477             }else if(p == "below"){
33478                 t.parentNode.insertBefore(n, t.nextSibling);
33479             }else{
33480                 t.appendChild(n);
33481             }
33482         }
33483         n.ui.focus();
33484         if(this.tree.hlDrop){
33485             n.ui.highlight();
33486         }
33487         t.ui.endDrop();
33488         this.tree.fireEvent("nodedrop", de);
33489     },
33490     
33491     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33492         if(this.tree.hlDrop){
33493             dropNode.ui.focus();
33494             dropNode.ui.highlight();
33495         }
33496         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33497     },
33498     
33499     getTree : function(){
33500         return this.tree;
33501     },
33502     
33503     removeDropIndicators : function(n){
33504         if(n && n.ddel){
33505             var el = n.ddel;
33506             Roo.fly(el).removeClass([
33507                     "x-tree-drag-insert-above",
33508                     "x-tree-drag-insert-below",
33509                     "x-tree-drag-append"]);
33510             this.lastInsertClass = "_noclass";
33511         }
33512     },
33513     
33514     beforeDragDrop : function(target, e, id){
33515         this.cancelExpand();
33516         return true;
33517     },
33518     
33519     afterRepair : function(data){
33520         if(data && Roo.enableFx){
33521             data.node.ui.highlight();
33522         }
33523         this.hideProxy();
33524     }    
33525 });
33526
33527 }
33528 /*
33529  * Based on:
33530  * Ext JS Library 1.1.1
33531  * Copyright(c) 2006-2007, Ext JS, LLC.
33532  *
33533  * Originally Released Under LGPL - original licence link has changed is not relivant.
33534  *
33535  * Fork - LGPL
33536  * <script type="text/javascript">
33537  */
33538  
33539
33540 if(Roo.dd.DragZone){
33541 Roo.tree.TreeDragZone = function(tree, config){
33542     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33543     this.tree = tree;
33544 };
33545
33546 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33547     ddGroup : "TreeDD",
33548     
33549     onBeforeDrag : function(data, e){
33550         var n = data.node;
33551         return n && n.draggable && !n.disabled;
33552     },
33553     
33554     onInitDrag : function(e){
33555         var data = this.dragData;
33556         this.tree.getSelectionModel().select(data.node);
33557         this.proxy.update("");
33558         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33559         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33560     },
33561     
33562     getRepairXY : function(e, data){
33563         return data.node.ui.getDDRepairXY();
33564     },
33565     
33566     onEndDrag : function(data, e){
33567         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33568     },
33569     
33570     onValidDrop : function(dd, e, id){
33571         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33572         this.hideProxy();
33573     },
33574     
33575     beforeInvalidDrop : function(e, id){
33576         // this scrolls the original position back into view
33577         var sm = this.tree.getSelectionModel();
33578         sm.clearSelections();
33579         sm.select(this.dragData.node);
33580     }
33581 });
33582 }/*
33583  * Based on:
33584  * Ext JS Library 1.1.1
33585  * Copyright(c) 2006-2007, Ext JS, LLC.
33586  *
33587  * Originally Released Under LGPL - original licence link has changed is not relivant.
33588  *
33589  * Fork - LGPL
33590  * <script type="text/javascript">
33591  */
33592 /**
33593  * @class Roo.tree.TreeEditor
33594  * @extends Roo.Editor
33595  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33596  * as the editor field.
33597  * @constructor
33598  * @param {Object} config (used to be the tree panel.)
33599  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33600  * 
33601  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33602  * @cfg {Roo.form.TextField|Object} field The field configuration
33603  *
33604  * 
33605  */
33606 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33607     var tree = config;
33608     var field;
33609     if (oldconfig) { // old style..
33610         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33611     } else {
33612         // new style..
33613         tree = config.tree;
33614         config.field = config.field  || {};
33615         config.field.xtype = 'TextField';
33616         field = Roo.factory(config.field, Roo.form);
33617     }
33618     config = config || {};
33619     
33620     
33621     this.addEvents({
33622         /**
33623          * @event beforenodeedit
33624          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33625          * false from the handler of this event.
33626          * @param {Editor} this
33627          * @param {Roo.tree.Node} node 
33628          */
33629         "beforenodeedit" : true
33630     });
33631     
33632     //Roo.log(config);
33633     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33634
33635     this.tree = tree;
33636
33637     tree.on('beforeclick', this.beforeNodeClick, this);
33638     tree.getTreeEl().on('mousedown', this.hide, this);
33639     this.on('complete', this.updateNode, this);
33640     this.on('beforestartedit', this.fitToTree, this);
33641     this.on('startedit', this.bindScroll, this, {delay:10});
33642     this.on('specialkey', this.onSpecialKey, this);
33643 };
33644
33645 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33646     /**
33647      * @cfg {String} alignment
33648      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33649      */
33650     alignment: "l-l",
33651     // inherit
33652     autoSize: false,
33653     /**
33654      * @cfg {Boolean} hideEl
33655      * True to hide the bound element while the editor is displayed (defaults to false)
33656      */
33657     hideEl : false,
33658     /**
33659      * @cfg {String} cls
33660      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33661      */
33662     cls: "x-small-editor x-tree-editor",
33663     /**
33664      * @cfg {Boolean} shim
33665      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33666      */
33667     shim:false,
33668     // inherit
33669     shadow:"frame",
33670     /**
33671      * @cfg {Number} maxWidth
33672      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33673      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33674      * scroll and client offsets into account prior to each edit.
33675      */
33676     maxWidth: 250,
33677
33678     editDelay : 350,
33679
33680     // private
33681     fitToTree : function(ed, el){
33682         var td = this.tree.getTreeEl().dom, nd = el.dom;
33683         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33684             td.scrollLeft = nd.offsetLeft;
33685         }
33686         var w = Math.min(
33687                 this.maxWidth,
33688                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33689         this.setSize(w, '');
33690         
33691         return this.fireEvent('beforenodeedit', this, this.editNode);
33692         
33693     },
33694
33695     // private
33696     triggerEdit : function(node){
33697         this.completeEdit();
33698         this.editNode = node;
33699         this.startEdit(node.ui.textNode, node.text);
33700     },
33701
33702     // private
33703     bindScroll : function(){
33704         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33705     },
33706
33707     // private
33708     beforeNodeClick : function(node, e){
33709         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33710         this.lastClick = new Date();
33711         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33712             e.stopEvent();
33713             this.triggerEdit(node);
33714             return false;
33715         }
33716         return true;
33717     },
33718
33719     // private
33720     updateNode : function(ed, value){
33721         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33722         this.editNode.setText(value);
33723     },
33724
33725     // private
33726     onHide : function(){
33727         Roo.tree.TreeEditor.superclass.onHide.call(this);
33728         if(this.editNode){
33729             this.editNode.ui.focus();
33730         }
33731     },
33732
33733     // private
33734     onSpecialKey : function(field, e){
33735         var k = e.getKey();
33736         if(k == e.ESC){
33737             e.stopEvent();
33738             this.cancelEdit();
33739         }else if(k == e.ENTER && !e.hasModifier()){
33740             e.stopEvent();
33741             this.completeEdit();
33742         }
33743     }
33744 });//<Script type="text/javascript">
33745 /*
33746  * Based on:
33747  * Ext JS Library 1.1.1
33748  * Copyright(c) 2006-2007, Ext JS, LLC.
33749  *
33750  * Originally Released Under LGPL - original licence link has changed is not relivant.
33751  *
33752  * Fork - LGPL
33753  * <script type="text/javascript">
33754  */
33755  
33756 /**
33757  * Not documented??? - probably should be...
33758  */
33759
33760 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33761     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33762     
33763     renderElements : function(n, a, targetNode, bulkRender){
33764         //consel.log("renderElements?");
33765         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33766
33767         var t = n.getOwnerTree();
33768         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33769         
33770         var cols = t.columns;
33771         var bw = t.borderWidth;
33772         var c = cols[0];
33773         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33774          var cb = typeof a.checked == "boolean";
33775         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33776         var colcls = 'x-t-' + tid + '-c0';
33777         var buf = [
33778             '<li class="x-tree-node">',
33779             
33780                 
33781                 '<div class="x-tree-node-el ', a.cls,'">',
33782                     // extran...
33783                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33784                 
33785                 
33786                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33787                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33788                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33789                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33790                            (a.iconCls ? ' '+a.iconCls : ''),
33791                            '" unselectable="on" />',
33792                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33793                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33794                              
33795                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33796                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33797                             '<span unselectable="on" qtip="' + tx + '">',
33798                              tx,
33799                              '</span></a>' ,
33800                     '</div>',
33801                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33802                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33803                  ];
33804         for(var i = 1, len = cols.length; i < len; i++){
33805             c = cols[i];
33806             colcls = 'x-t-' + tid + '-c' +i;
33807             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33808             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33809                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33810                       "</div>");
33811          }
33812          
33813          buf.push(
33814             '</a>',
33815             '<div class="x-clear"></div></div>',
33816             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33817             "</li>");
33818         
33819         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33820             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33821                                 n.nextSibling.ui.getEl(), buf.join(""));
33822         }else{
33823             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33824         }
33825         var el = this.wrap.firstChild;
33826         this.elRow = el;
33827         this.elNode = el.firstChild;
33828         this.ranchor = el.childNodes[1];
33829         this.ctNode = this.wrap.childNodes[1];
33830         var cs = el.firstChild.childNodes;
33831         this.indentNode = cs[0];
33832         this.ecNode = cs[1];
33833         this.iconNode = cs[2];
33834         var index = 3;
33835         if(cb){
33836             this.checkbox = cs[3];
33837             index++;
33838         }
33839         this.anchor = cs[index];
33840         
33841         this.textNode = cs[index].firstChild;
33842         
33843         //el.on("click", this.onClick, this);
33844         //el.on("dblclick", this.onDblClick, this);
33845         
33846         
33847        // console.log(this);
33848     },
33849     initEvents : function(){
33850         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33851         
33852             
33853         var a = this.ranchor;
33854
33855         var el = Roo.get(a);
33856
33857         if(Roo.isOpera){ // opera render bug ignores the CSS
33858             el.setStyle("text-decoration", "none");
33859         }
33860
33861         el.on("click", this.onClick, this);
33862         el.on("dblclick", this.onDblClick, this);
33863         el.on("contextmenu", this.onContextMenu, this);
33864         
33865     },
33866     
33867     /*onSelectedChange : function(state){
33868         if(state){
33869             this.focus();
33870             this.addClass("x-tree-selected");
33871         }else{
33872             //this.blur();
33873             this.removeClass("x-tree-selected");
33874         }
33875     },*/
33876     addClass : function(cls){
33877         if(this.elRow){
33878             Roo.fly(this.elRow).addClass(cls);
33879         }
33880         
33881     },
33882     
33883     
33884     removeClass : function(cls){
33885         if(this.elRow){
33886             Roo.fly(this.elRow).removeClass(cls);
33887         }
33888     }
33889
33890     
33891     
33892 });//<Script type="text/javascript">
33893
33894 /*
33895  * Based on:
33896  * Ext JS Library 1.1.1
33897  * Copyright(c) 2006-2007, Ext JS, LLC.
33898  *
33899  * Originally Released Under LGPL - original licence link has changed is not relivant.
33900  *
33901  * Fork - LGPL
33902  * <script type="text/javascript">
33903  */
33904  
33905
33906 /**
33907  * @class Roo.tree.ColumnTree
33908  * @extends Roo.data.TreePanel
33909  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33910  * @cfg {int} borderWidth  compined right/left border allowance
33911  * @constructor
33912  * @param {String/HTMLElement/Element} el The container element
33913  * @param {Object} config
33914  */
33915 Roo.tree.ColumnTree =  function(el, config)
33916 {
33917    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33918    this.addEvents({
33919         /**
33920         * @event resize
33921         * Fire this event on a container when it resizes
33922         * @param {int} w Width
33923         * @param {int} h Height
33924         */
33925        "resize" : true
33926     });
33927     this.on('resize', this.onResize, this);
33928 };
33929
33930 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33931     //lines:false,
33932     
33933     
33934     borderWidth: Roo.isBorderBox ? 0 : 2, 
33935     headEls : false,
33936     
33937     render : function(){
33938         // add the header.....
33939        
33940         Roo.tree.ColumnTree.superclass.render.apply(this);
33941         
33942         this.el.addClass('x-column-tree');
33943         
33944         this.headers = this.el.createChild(
33945             {cls:'x-tree-headers'},this.innerCt.dom);
33946    
33947         var cols = this.columns, c;
33948         var totalWidth = 0;
33949         this.headEls = [];
33950         var  len = cols.length;
33951         for(var i = 0; i < len; i++){
33952              c = cols[i];
33953              totalWidth += c.width;
33954             this.headEls.push(this.headers.createChild({
33955                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33956                  cn: {
33957                      cls:'x-tree-hd-text',
33958                      html: c.header
33959                  },
33960                  style:'width:'+(c.width-this.borderWidth)+'px;'
33961              }));
33962         }
33963         this.headers.createChild({cls:'x-clear'});
33964         // prevent floats from wrapping when clipped
33965         this.headers.setWidth(totalWidth);
33966         //this.innerCt.setWidth(totalWidth);
33967         this.innerCt.setStyle({ overflow: 'auto' });
33968         this.onResize(this.width, this.height);
33969              
33970         
33971     },
33972     onResize : function(w,h)
33973     {
33974         this.height = h;
33975         this.width = w;
33976         // resize cols..
33977         this.innerCt.setWidth(this.width);
33978         this.innerCt.setHeight(this.height-20);
33979         
33980         // headers...
33981         var cols = this.columns, c;
33982         var totalWidth = 0;
33983         var expEl = false;
33984         var len = cols.length;
33985         for(var i = 0; i < len; i++){
33986             c = cols[i];
33987             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33988                 // it's the expander..
33989                 expEl  = this.headEls[i];
33990                 continue;
33991             }
33992             totalWidth += c.width;
33993             
33994         }
33995         if (expEl) {
33996             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33997         }
33998         this.headers.setWidth(w-20);
33999
34000         
34001         
34002         
34003     }
34004 });
34005 /*
34006  * Based on:
34007  * Ext JS Library 1.1.1
34008  * Copyright(c) 2006-2007, Ext JS, LLC.
34009  *
34010  * Originally Released Under LGPL - original licence link has changed is not relivant.
34011  *
34012  * Fork - LGPL
34013  * <script type="text/javascript">
34014  */
34015  
34016 /**
34017  * @class Roo.menu.Menu
34018  * @extends Roo.util.Observable
34019  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34020  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34021  * @constructor
34022  * Creates a new Menu
34023  * @param {Object} config Configuration options
34024  */
34025 Roo.menu.Menu = function(config){
34026     Roo.apply(this, config);
34027     this.id = this.id || Roo.id();
34028     this.addEvents({
34029         /**
34030          * @event beforeshow
34031          * Fires before this menu is displayed
34032          * @param {Roo.menu.Menu} this
34033          */
34034         beforeshow : true,
34035         /**
34036          * @event beforehide
34037          * Fires before this menu is hidden
34038          * @param {Roo.menu.Menu} this
34039          */
34040         beforehide : true,
34041         /**
34042          * @event show
34043          * Fires after this menu is displayed
34044          * @param {Roo.menu.Menu} this
34045          */
34046         show : true,
34047         /**
34048          * @event hide
34049          * Fires after this menu is hidden
34050          * @param {Roo.menu.Menu} this
34051          */
34052         hide : true,
34053         /**
34054          * @event click
34055          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34056          * @param {Roo.menu.Menu} this
34057          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34058          * @param {Roo.EventObject} e
34059          */
34060         click : true,
34061         /**
34062          * @event mouseover
34063          * Fires when the mouse is hovering over this menu
34064          * @param {Roo.menu.Menu} this
34065          * @param {Roo.EventObject} e
34066          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34067          */
34068         mouseover : true,
34069         /**
34070          * @event mouseout
34071          * Fires when the mouse exits this menu
34072          * @param {Roo.menu.Menu} this
34073          * @param {Roo.EventObject} e
34074          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34075          */
34076         mouseout : true,
34077         /**
34078          * @event itemclick
34079          * Fires when a menu item contained in this menu is clicked
34080          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34081          * @param {Roo.EventObject} e
34082          */
34083         itemclick: true
34084     });
34085     if (this.registerMenu) {
34086         Roo.menu.MenuMgr.register(this);
34087     }
34088     
34089     var mis = this.items;
34090     this.items = new Roo.util.MixedCollection();
34091     if(mis){
34092         this.add.apply(this, mis);
34093     }
34094 };
34095
34096 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34097     /**
34098      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34099      */
34100     minWidth : 120,
34101     /**
34102      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34103      * for bottom-right shadow (defaults to "sides")
34104      */
34105     shadow : "sides",
34106     /**
34107      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34108      * this menu (defaults to "tl-tr?")
34109      */
34110     subMenuAlign : "tl-tr?",
34111     /**
34112      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34113      * relative to its element of origin (defaults to "tl-bl?")
34114      */
34115     defaultAlign : "tl-bl?",
34116     /**
34117      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34118      */
34119     allowOtherMenus : false,
34120     /**
34121      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34122      */
34123     registerMenu : true,
34124
34125     hidden:true,
34126
34127     // private
34128     render : function(){
34129         if(this.el){
34130             return;
34131         }
34132         var el = this.el = new Roo.Layer({
34133             cls: "x-menu",
34134             shadow:this.shadow,
34135             constrain: false,
34136             parentEl: this.parentEl || document.body,
34137             zindex:15000
34138         });
34139
34140         this.keyNav = new Roo.menu.MenuNav(this);
34141
34142         if(this.plain){
34143             el.addClass("x-menu-plain");
34144         }
34145         if(this.cls){
34146             el.addClass(this.cls);
34147         }
34148         // generic focus element
34149         this.focusEl = el.createChild({
34150             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34151         });
34152         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34153         ul.on("click", this.onClick, this);
34154         ul.on("mouseover", this.onMouseOver, this);
34155         ul.on("mouseout", this.onMouseOut, this);
34156         this.items.each(function(item){
34157             var li = document.createElement("li");
34158             li.className = "x-menu-list-item";
34159             ul.dom.appendChild(li);
34160             item.render(li, this);
34161         }, this);
34162         this.ul = ul;
34163         this.autoWidth();
34164     },
34165
34166     // private
34167     autoWidth : function(){
34168         var el = this.el, ul = this.ul;
34169         if(!el){
34170             return;
34171         }
34172         var w = this.width;
34173         if(w){
34174             el.setWidth(w);
34175         }else if(Roo.isIE){
34176             el.setWidth(this.minWidth);
34177             var t = el.dom.offsetWidth; // force recalc
34178             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34179         }
34180     },
34181
34182     // private
34183     delayAutoWidth : function(){
34184         if(this.rendered){
34185             if(!this.awTask){
34186                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34187             }
34188             this.awTask.delay(20);
34189         }
34190     },
34191
34192     // private
34193     findTargetItem : function(e){
34194         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34195         if(t && t.menuItemId){
34196             return this.items.get(t.menuItemId);
34197         }
34198     },
34199
34200     // private
34201     onClick : function(e){
34202         var t;
34203         if(t = this.findTargetItem(e)){
34204             t.onClick(e);
34205             this.fireEvent("click", this, t, e);
34206         }
34207     },
34208
34209     // private
34210     setActiveItem : function(item, autoExpand){
34211         if(item != this.activeItem){
34212             if(this.activeItem){
34213                 this.activeItem.deactivate();
34214             }
34215             this.activeItem = item;
34216             item.activate(autoExpand);
34217         }else if(autoExpand){
34218             item.expandMenu();
34219         }
34220     },
34221
34222     // private
34223     tryActivate : function(start, step){
34224         var items = this.items;
34225         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34226             var item = items.get(i);
34227             if(!item.disabled && item.canActivate){
34228                 this.setActiveItem(item, false);
34229                 return item;
34230             }
34231         }
34232         return false;
34233     },
34234
34235     // private
34236     onMouseOver : function(e){
34237         var t;
34238         if(t = this.findTargetItem(e)){
34239             if(t.canActivate && !t.disabled){
34240                 this.setActiveItem(t, true);
34241             }
34242         }
34243         this.fireEvent("mouseover", this, e, t);
34244     },
34245
34246     // private
34247     onMouseOut : function(e){
34248         var t;
34249         if(t = this.findTargetItem(e)){
34250             if(t == this.activeItem && t.shouldDeactivate(e)){
34251                 this.activeItem.deactivate();
34252                 delete this.activeItem;
34253             }
34254         }
34255         this.fireEvent("mouseout", this, e, t);
34256     },
34257
34258     /**
34259      * Read-only.  Returns true if the menu is currently displayed, else false.
34260      * @type Boolean
34261      */
34262     isVisible : function(){
34263         return this.el && !this.hidden;
34264     },
34265
34266     /**
34267      * Displays this menu relative to another element
34268      * @param {String/HTMLElement/Roo.Element} element The element to align to
34269      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34270      * the element (defaults to this.defaultAlign)
34271      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34272      */
34273     show : function(el, pos, parentMenu){
34274         this.parentMenu = parentMenu;
34275         if(!this.el){
34276             this.render();
34277         }
34278         this.fireEvent("beforeshow", this);
34279         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34280     },
34281
34282     /**
34283      * Displays this menu at a specific xy position
34284      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34285      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34286      */
34287     showAt : function(xy, parentMenu, /* private: */_e){
34288         this.parentMenu = parentMenu;
34289         if(!this.el){
34290             this.render();
34291         }
34292         if(_e !== false){
34293             this.fireEvent("beforeshow", this);
34294             xy = this.el.adjustForConstraints(xy);
34295         }
34296         this.el.setXY(xy);
34297         this.el.show();
34298         this.hidden = false;
34299         this.focus();
34300         this.fireEvent("show", this);
34301     },
34302
34303     focus : function(){
34304         if(!this.hidden){
34305             this.doFocus.defer(50, this);
34306         }
34307     },
34308
34309     doFocus : function(){
34310         if(!this.hidden){
34311             this.focusEl.focus();
34312         }
34313     },
34314
34315     /**
34316      * Hides this menu and optionally all parent menus
34317      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34318      */
34319     hide : function(deep){
34320         if(this.el && this.isVisible()){
34321             this.fireEvent("beforehide", this);
34322             if(this.activeItem){
34323                 this.activeItem.deactivate();
34324                 this.activeItem = null;
34325             }
34326             this.el.hide();
34327             this.hidden = true;
34328             this.fireEvent("hide", this);
34329         }
34330         if(deep === true && this.parentMenu){
34331             this.parentMenu.hide(true);
34332         }
34333     },
34334
34335     /**
34336      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34337      * Any of the following are valid:
34338      * <ul>
34339      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34340      * <li>An HTMLElement object which will be converted to a menu item</li>
34341      * <li>A menu item config object that will be created as a new menu item</li>
34342      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34343      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34344      * </ul>
34345      * Usage:
34346      * <pre><code>
34347 // Create the menu
34348 var menu = new Roo.menu.Menu();
34349
34350 // Create a menu item to add by reference
34351 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34352
34353 // Add a bunch of items at once using different methods.
34354 // Only the last item added will be returned.
34355 var item = menu.add(
34356     menuItem,                // add existing item by ref
34357     'Dynamic Item',          // new TextItem
34358     '-',                     // new separator
34359     { text: 'Config Item' }  // new item by config
34360 );
34361 </code></pre>
34362      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34363      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34364      */
34365     add : function(){
34366         var a = arguments, l = a.length, item;
34367         for(var i = 0; i < l; i++){
34368             var el = a[i];
34369             if ((typeof(el) == "object") && el.xtype && el.xns) {
34370                 el = Roo.factory(el, Roo.menu);
34371             }
34372             
34373             if(el.render){ // some kind of Item
34374                 item = this.addItem(el);
34375             }else if(typeof el == "string"){ // string
34376                 if(el == "separator" || el == "-"){
34377                     item = this.addSeparator();
34378                 }else{
34379                     item = this.addText(el);
34380                 }
34381             }else if(el.tagName || el.el){ // element
34382                 item = this.addElement(el);
34383             }else if(typeof el == "object"){ // must be menu item config?
34384                 item = this.addMenuItem(el);
34385             }
34386         }
34387         return item;
34388     },
34389
34390     /**
34391      * Returns this menu's underlying {@link Roo.Element} object
34392      * @return {Roo.Element} The element
34393      */
34394     getEl : function(){
34395         if(!this.el){
34396             this.render();
34397         }
34398         return this.el;
34399     },
34400
34401     /**
34402      * Adds a separator bar to the menu
34403      * @return {Roo.menu.Item} The menu item that was added
34404      */
34405     addSeparator : function(){
34406         return this.addItem(new Roo.menu.Separator());
34407     },
34408
34409     /**
34410      * Adds an {@link Roo.Element} object to the menu
34411      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34412      * @return {Roo.menu.Item} The menu item that was added
34413      */
34414     addElement : function(el){
34415         return this.addItem(new Roo.menu.BaseItem(el));
34416     },
34417
34418     /**
34419      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34420      * @param {Roo.menu.Item} item The menu item to add
34421      * @return {Roo.menu.Item} The menu item that was added
34422      */
34423     addItem : function(item){
34424         this.items.add(item);
34425         if(this.ul){
34426             var li = document.createElement("li");
34427             li.className = "x-menu-list-item";
34428             this.ul.dom.appendChild(li);
34429             item.render(li, this);
34430             this.delayAutoWidth();
34431         }
34432         return item;
34433     },
34434
34435     /**
34436      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34437      * @param {Object} config A MenuItem config object
34438      * @return {Roo.menu.Item} The menu item that was added
34439      */
34440     addMenuItem : function(config){
34441         if(!(config instanceof Roo.menu.Item)){
34442             if(typeof config.checked == "boolean"){ // must be check menu item config?
34443                 config = new Roo.menu.CheckItem(config);
34444             }else{
34445                 config = new Roo.menu.Item(config);
34446             }
34447         }
34448         return this.addItem(config);
34449     },
34450
34451     /**
34452      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34453      * @param {String} text The text to display in the menu item
34454      * @return {Roo.menu.Item} The menu item that was added
34455      */
34456     addText : function(text){
34457         return this.addItem(new Roo.menu.TextItem({ text : text }));
34458     },
34459
34460     /**
34461      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34462      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34463      * @param {Roo.menu.Item} item The menu item to add
34464      * @return {Roo.menu.Item} The menu item that was added
34465      */
34466     insert : function(index, item){
34467         this.items.insert(index, item);
34468         if(this.ul){
34469             var li = document.createElement("li");
34470             li.className = "x-menu-list-item";
34471             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34472             item.render(li, this);
34473             this.delayAutoWidth();
34474         }
34475         return item;
34476     },
34477
34478     /**
34479      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34480      * @param {Roo.menu.Item} item The menu item to remove
34481      */
34482     remove : function(item){
34483         this.items.removeKey(item.id);
34484         item.destroy();
34485     },
34486
34487     /**
34488      * Removes and destroys all items in the menu
34489      */
34490     removeAll : function(){
34491         var f;
34492         while(f = this.items.first()){
34493             this.remove(f);
34494         }
34495     }
34496 });
34497
34498 // MenuNav is a private utility class used internally by the Menu
34499 Roo.menu.MenuNav = function(menu){
34500     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34501     this.scope = this.menu = menu;
34502 };
34503
34504 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34505     doRelay : function(e, h){
34506         var k = e.getKey();
34507         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34508             this.menu.tryActivate(0, 1);
34509             return false;
34510         }
34511         return h.call(this.scope || this, e, this.menu);
34512     },
34513
34514     up : function(e, m){
34515         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34516             m.tryActivate(m.items.length-1, -1);
34517         }
34518     },
34519
34520     down : function(e, m){
34521         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34522             m.tryActivate(0, 1);
34523         }
34524     },
34525
34526     right : function(e, m){
34527         if(m.activeItem){
34528             m.activeItem.expandMenu(true);
34529         }
34530     },
34531
34532     left : function(e, m){
34533         m.hide();
34534         if(m.parentMenu && m.parentMenu.activeItem){
34535             m.parentMenu.activeItem.activate();
34536         }
34537     },
34538
34539     enter : function(e, m){
34540         if(m.activeItem){
34541             e.stopPropagation();
34542             m.activeItem.onClick(e);
34543             m.fireEvent("click", this, m.activeItem);
34544             return true;
34545         }
34546     }
34547 });/*
34548  * Based on:
34549  * Ext JS Library 1.1.1
34550  * Copyright(c) 2006-2007, Ext JS, LLC.
34551  *
34552  * Originally Released Under LGPL - original licence link has changed is not relivant.
34553  *
34554  * Fork - LGPL
34555  * <script type="text/javascript">
34556  */
34557  
34558 /**
34559  * @class Roo.menu.MenuMgr
34560  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34561  * @singleton
34562  */
34563 Roo.menu.MenuMgr = function(){
34564    var menus, active, groups = {}, attached = false, lastShow = new Date();
34565
34566    // private - called when first menu is created
34567    function init(){
34568        menus = {};
34569        active = new Roo.util.MixedCollection();
34570        Roo.get(document).addKeyListener(27, function(){
34571            if(active.length > 0){
34572                hideAll();
34573            }
34574        });
34575    }
34576
34577    // private
34578    function hideAll(){
34579        if(active && active.length > 0){
34580            var c = active.clone();
34581            c.each(function(m){
34582                m.hide();
34583            });
34584        }
34585    }
34586
34587    // private
34588    function onHide(m){
34589        active.remove(m);
34590        if(active.length < 1){
34591            Roo.get(document).un("mousedown", onMouseDown);
34592            attached = false;
34593        }
34594    }
34595
34596    // private
34597    function onShow(m){
34598        var last = active.last();
34599        lastShow = new Date();
34600        active.add(m);
34601        if(!attached){
34602            Roo.get(document).on("mousedown", onMouseDown);
34603            attached = true;
34604        }
34605        if(m.parentMenu){
34606           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34607           m.parentMenu.activeChild = m;
34608        }else if(last && last.isVisible()){
34609           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34610        }
34611    }
34612
34613    // private
34614    function onBeforeHide(m){
34615        if(m.activeChild){
34616            m.activeChild.hide();
34617        }
34618        if(m.autoHideTimer){
34619            clearTimeout(m.autoHideTimer);
34620            delete m.autoHideTimer;
34621        }
34622    }
34623
34624    // private
34625    function onBeforeShow(m){
34626        var pm = m.parentMenu;
34627        if(!pm && !m.allowOtherMenus){
34628            hideAll();
34629        }else if(pm && pm.activeChild && active != m){
34630            pm.activeChild.hide();
34631        }
34632    }
34633
34634    // private
34635    function onMouseDown(e){
34636        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34637            hideAll();
34638        }
34639    }
34640
34641    // private
34642    function onBeforeCheck(mi, state){
34643        if(state){
34644            var g = groups[mi.group];
34645            for(var i = 0, l = g.length; i < l; i++){
34646                if(g[i] != mi){
34647                    g[i].setChecked(false);
34648                }
34649            }
34650        }
34651    }
34652
34653    return {
34654
34655        /**
34656         * Hides all menus that are currently visible
34657         */
34658        hideAll : function(){
34659             hideAll();  
34660        },
34661
34662        // private
34663        register : function(menu){
34664            if(!menus){
34665                init();
34666            }
34667            menus[menu.id] = menu;
34668            menu.on("beforehide", onBeforeHide);
34669            menu.on("hide", onHide);
34670            menu.on("beforeshow", onBeforeShow);
34671            menu.on("show", onShow);
34672            var g = menu.group;
34673            if(g && menu.events["checkchange"]){
34674                if(!groups[g]){
34675                    groups[g] = [];
34676                }
34677                groups[g].push(menu);
34678                menu.on("checkchange", onCheck);
34679            }
34680        },
34681
34682         /**
34683          * Returns a {@link Roo.menu.Menu} object
34684          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34685          * be used to generate and return a new Menu instance.
34686          */
34687        get : function(menu){
34688            if(typeof menu == "string"){ // menu id
34689                return menus[menu];
34690            }else if(menu.events){  // menu instance
34691                return menu;
34692            }else if(typeof menu.length == 'number'){ // array of menu items?
34693                return new Roo.menu.Menu({items:menu});
34694            }else{ // otherwise, must be a config
34695                return new Roo.menu.Menu(menu);
34696            }
34697        },
34698
34699        // private
34700        unregister : function(menu){
34701            delete menus[menu.id];
34702            menu.un("beforehide", onBeforeHide);
34703            menu.un("hide", onHide);
34704            menu.un("beforeshow", onBeforeShow);
34705            menu.un("show", onShow);
34706            var g = menu.group;
34707            if(g && menu.events["checkchange"]){
34708                groups[g].remove(menu);
34709                menu.un("checkchange", onCheck);
34710            }
34711        },
34712
34713        // private
34714        registerCheckable : function(menuItem){
34715            var g = menuItem.group;
34716            if(g){
34717                if(!groups[g]){
34718                    groups[g] = [];
34719                }
34720                groups[g].push(menuItem);
34721                menuItem.on("beforecheckchange", onBeforeCheck);
34722            }
34723        },
34724
34725        // private
34726        unregisterCheckable : function(menuItem){
34727            var g = menuItem.group;
34728            if(g){
34729                groups[g].remove(menuItem);
34730                menuItem.un("beforecheckchange", onBeforeCheck);
34731            }
34732        }
34733    };
34734 }();/*
34735  * Based on:
34736  * Ext JS Library 1.1.1
34737  * Copyright(c) 2006-2007, Ext JS, LLC.
34738  *
34739  * Originally Released Under LGPL - original licence link has changed is not relivant.
34740  *
34741  * Fork - LGPL
34742  * <script type="text/javascript">
34743  */
34744  
34745
34746 /**
34747  * @class Roo.menu.BaseItem
34748  * @extends Roo.Component
34749  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34750  * management and base configuration options shared by all menu components.
34751  * @constructor
34752  * Creates a new BaseItem
34753  * @param {Object} config Configuration options
34754  */
34755 Roo.menu.BaseItem = function(config){
34756     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34757
34758     this.addEvents({
34759         /**
34760          * @event click
34761          * Fires when this item is clicked
34762          * @param {Roo.menu.BaseItem} this
34763          * @param {Roo.EventObject} e
34764          */
34765         click: true,
34766         /**
34767          * @event activate
34768          * Fires when this item is activated
34769          * @param {Roo.menu.BaseItem} this
34770          */
34771         activate : true,
34772         /**
34773          * @event deactivate
34774          * Fires when this item is deactivated
34775          * @param {Roo.menu.BaseItem} this
34776          */
34777         deactivate : true
34778     });
34779
34780     if(this.handler){
34781         this.on("click", this.handler, this.scope, true);
34782     }
34783 };
34784
34785 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34786     /**
34787      * @cfg {Function} handler
34788      * A function that will handle the click event of this menu item (defaults to undefined)
34789      */
34790     /**
34791      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34792      */
34793     canActivate : false,
34794     /**
34795      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34796      */
34797     activeClass : "x-menu-item-active",
34798     /**
34799      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34800      */
34801     hideOnClick : true,
34802     /**
34803      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34804      */
34805     hideDelay : 100,
34806
34807     // private
34808     ctype: "Roo.menu.BaseItem",
34809
34810     // private
34811     actionMode : "container",
34812
34813     // private
34814     render : function(container, parentMenu){
34815         this.parentMenu = parentMenu;
34816         Roo.menu.BaseItem.superclass.render.call(this, container);
34817         this.container.menuItemId = this.id;
34818     },
34819
34820     // private
34821     onRender : function(container, position){
34822         this.el = Roo.get(this.el);
34823         container.dom.appendChild(this.el.dom);
34824     },
34825
34826     // private
34827     onClick : function(e){
34828         if(!this.disabled && this.fireEvent("click", this, e) !== false
34829                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34830             this.handleClick(e);
34831         }else{
34832             e.stopEvent();
34833         }
34834     },
34835
34836     // private
34837     activate : function(){
34838         if(this.disabled){
34839             return false;
34840         }
34841         var li = this.container;
34842         li.addClass(this.activeClass);
34843         this.region = li.getRegion().adjust(2, 2, -2, -2);
34844         this.fireEvent("activate", this);
34845         return true;
34846     },
34847
34848     // private
34849     deactivate : function(){
34850         this.container.removeClass(this.activeClass);
34851         this.fireEvent("deactivate", this);
34852     },
34853
34854     // private
34855     shouldDeactivate : function(e){
34856         return !this.region || !this.region.contains(e.getPoint());
34857     },
34858
34859     // private
34860     handleClick : function(e){
34861         if(this.hideOnClick){
34862             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34863         }
34864     },
34865
34866     // private
34867     expandMenu : function(autoActivate){
34868         // do nothing
34869     },
34870
34871     // private
34872     hideMenu : function(){
34873         // do nothing
34874     }
34875 });/*
34876  * Based on:
34877  * Ext JS Library 1.1.1
34878  * Copyright(c) 2006-2007, Ext JS, LLC.
34879  *
34880  * Originally Released Under LGPL - original licence link has changed is not relivant.
34881  *
34882  * Fork - LGPL
34883  * <script type="text/javascript">
34884  */
34885  
34886 /**
34887  * @class Roo.menu.Adapter
34888  * @extends Roo.menu.BaseItem
34889  * 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.
34890  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34891  * @constructor
34892  * Creates a new Adapter
34893  * @param {Object} config Configuration options
34894  */
34895 Roo.menu.Adapter = function(component, config){
34896     Roo.menu.Adapter.superclass.constructor.call(this, config);
34897     this.component = component;
34898 };
34899 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34900     // private
34901     canActivate : true,
34902
34903     // private
34904     onRender : function(container, position){
34905         this.component.render(container);
34906         this.el = this.component.getEl();
34907     },
34908
34909     // private
34910     activate : function(){
34911         if(this.disabled){
34912             return false;
34913         }
34914         this.component.focus();
34915         this.fireEvent("activate", this);
34916         return true;
34917     },
34918
34919     // private
34920     deactivate : function(){
34921         this.fireEvent("deactivate", this);
34922     },
34923
34924     // private
34925     disable : function(){
34926         this.component.disable();
34927         Roo.menu.Adapter.superclass.disable.call(this);
34928     },
34929
34930     // private
34931     enable : function(){
34932         this.component.enable();
34933         Roo.menu.Adapter.superclass.enable.call(this);
34934     }
34935 });/*
34936  * Based on:
34937  * Ext JS Library 1.1.1
34938  * Copyright(c) 2006-2007, Ext JS, LLC.
34939  *
34940  * Originally Released Under LGPL - original licence link has changed is not relivant.
34941  *
34942  * Fork - LGPL
34943  * <script type="text/javascript">
34944  */
34945
34946 /**
34947  * @class Roo.menu.TextItem
34948  * @extends Roo.menu.BaseItem
34949  * Adds a static text string to a menu, usually used as either a heading or group separator.
34950  * Note: old style constructor with text is still supported.
34951  * 
34952  * @constructor
34953  * Creates a new TextItem
34954  * @param {Object} cfg Configuration
34955  */
34956 Roo.menu.TextItem = function(cfg){
34957     if (typeof(cfg) == 'string') {
34958         this.text = cfg;
34959     } else {
34960         Roo.apply(this,cfg);
34961     }
34962     
34963     Roo.menu.TextItem.superclass.constructor.call(this);
34964 };
34965
34966 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34967     /**
34968      * @cfg {Boolean} text Text to show on item.
34969      */
34970     text : '',
34971     
34972     /**
34973      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34974      */
34975     hideOnClick : false,
34976     /**
34977      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34978      */
34979     itemCls : "x-menu-text",
34980
34981     // private
34982     onRender : function(){
34983         var s = document.createElement("span");
34984         s.className = this.itemCls;
34985         s.innerHTML = this.text;
34986         this.el = s;
34987         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34988     }
34989 });/*
34990  * Based on:
34991  * Ext JS Library 1.1.1
34992  * Copyright(c) 2006-2007, Ext JS, LLC.
34993  *
34994  * Originally Released Under LGPL - original licence link has changed is not relivant.
34995  *
34996  * Fork - LGPL
34997  * <script type="text/javascript">
34998  */
34999
35000 /**
35001  * @class Roo.menu.Separator
35002  * @extends Roo.menu.BaseItem
35003  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35004  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35005  * @constructor
35006  * @param {Object} config Configuration options
35007  */
35008 Roo.menu.Separator = function(config){
35009     Roo.menu.Separator.superclass.constructor.call(this, config);
35010 };
35011
35012 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35013     /**
35014      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35015      */
35016     itemCls : "x-menu-sep",
35017     /**
35018      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35019      */
35020     hideOnClick : false,
35021
35022     // private
35023     onRender : function(li){
35024         var s = document.createElement("span");
35025         s.className = this.itemCls;
35026         s.innerHTML = "&#160;";
35027         this.el = s;
35028         li.addClass("x-menu-sep-li");
35029         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35030     }
35031 });/*
35032  * Based on:
35033  * Ext JS Library 1.1.1
35034  * Copyright(c) 2006-2007, Ext JS, LLC.
35035  *
35036  * Originally Released Under LGPL - original licence link has changed is not relivant.
35037  *
35038  * Fork - LGPL
35039  * <script type="text/javascript">
35040  */
35041 /**
35042  * @class Roo.menu.Item
35043  * @extends Roo.menu.BaseItem
35044  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35045  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35046  * activation and click handling.
35047  * @constructor
35048  * Creates a new Item
35049  * @param {Object} config Configuration options
35050  */
35051 Roo.menu.Item = function(config){
35052     Roo.menu.Item.superclass.constructor.call(this, config);
35053     if(this.menu){
35054         this.menu = Roo.menu.MenuMgr.get(this.menu);
35055     }
35056 };
35057 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35058     
35059     /**
35060      * @cfg {String} text
35061      * The text to show on the menu item.
35062      */
35063     text: '',
35064      /**
35065      * @cfg {String} HTML to render in menu
35066      * The text to show on the menu item (HTML version).
35067      */
35068     html: '',
35069     /**
35070      * @cfg {String} icon
35071      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35072      */
35073     icon: undefined,
35074     /**
35075      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35076      */
35077     itemCls : "x-menu-item",
35078     /**
35079      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35080      */
35081     canActivate : true,
35082     /**
35083      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35084      */
35085     showDelay: 200,
35086     // doc'd in BaseItem
35087     hideDelay: 200,
35088
35089     // private
35090     ctype: "Roo.menu.Item",
35091     
35092     // private
35093     onRender : function(container, position){
35094         var el = document.createElement("a");
35095         el.hideFocus = true;
35096         el.unselectable = "on";
35097         el.href = this.href || "#";
35098         if(this.hrefTarget){
35099             el.target = this.hrefTarget;
35100         }
35101         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35102         
35103         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35104         
35105         el.innerHTML = String.format(
35106                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35107                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35108         this.el = el;
35109         Roo.menu.Item.superclass.onRender.call(this, container, position);
35110     },
35111
35112     /**
35113      * Sets the text to display in this menu item
35114      * @param {String} text The text to display
35115      * @param {Boolean} isHTML true to indicate text is pure html.
35116      */
35117     setText : function(text, isHTML){
35118         if (isHTML) {
35119             this.html = text;
35120         } else {
35121             this.text = text;
35122             this.html = '';
35123         }
35124         if(this.rendered){
35125             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35126      
35127             this.el.update(String.format(
35128                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35129                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35130             this.parentMenu.autoWidth();
35131         }
35132     },
35133
35134     // private
35135     handleClick : function(e){
35136         if(!this.href){ // if no link defined, stop the event automatically
35137             e.stopEvent();
35138         }
35139         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35140     },
35141
35142     // private
35143     activate : function(autoExpand){
35144         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35145             this.focus();
35146             if(autoExpand){
35147                 this.expandMenu();
35148             }
35149         }
35150         return true;
35151     },
35152
35153     // private
35154     shouldDeactivate : function(e){
35155         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35156             if(this.menu && this.menu.isVisible()){
35157                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35158             }
35159             return true;
35160         }
35161         return false;
35162     },
35163
35164     // private
35165     deactivate : function(){
35166         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35167         this.hideMenu();
35168     },
35169
35170     // private
35171     expandMenu : function(autoActivate){
35172         if(!this.disabled && this.menu){
35173             clearTimeout(this.hideTimer);
35174             delete this.hideTimer;
35175             if(!this.menu.isVisible() && !this.showTimer){
35176                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35177             }else if (this.menu.isVisible() && autoActivate){
35178                 this.menu.tryActivate(0, 1);
35179             }
35180         }
35181     },
35182
35183     // private
35184     deferExpand : function(autoActivate){
35185         delete this.showTimer;
35186         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35187         if(autoActivate){
35188             this.menu.tryActivate(0, 1);
35189         }
35190     },
35191
35192     // private
35193     hideMenu : function(){
35194         clearTimeout(this.showTimer);
35195         delete this.showTimer;
35196         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35197             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35198         }
35199     },
35200
35201     // private
35202     deferHide : function(){
35203         delete this.hideTimer;
35204         this.menu.hide();
35205     }
35206 });/*
35207  * Based on:
35208  * Ext JS Library 1.1.1
35209  * Copyright(c) 2006-2007, Ext JS, LLC.
35210  *
35211  * Originally Released Under LGPL - original licence link has changed is not relivant.
35212  *
35213  * Fork - LGPL
35214  * <script type="text/javascript">
35215  */
35216  
35217 /**
35218  * @class Roo.menu.CheckItem
35219  * @extends Roo.menu.Item
35220  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35221  * @constructor
35222  * Creates a new CheckItem
35223  * @param {Object} config Configuration options
35224  */
35225 Roo.menu.CheckItem = function(config){
35226     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35227     this.addEvents({
35228         /**
35229          * @event beforecheckchange
35230          * Fires before the checked value is set, providing an opportunity to cancel if needed
35231          * @param {Roo.menu.CheckItem} this
35232          * @param {Boolean} checked The new checked value that will be set
35233          */
35234         "beforecheckchange" : true,
35235         /**
35236          * @event checkchange
35237          * Fires after the checked value has been set
35238          * @param {Roo.menu.CheckItem} this
35239          * @param {Boolean} checked The checked value that was set
35240          */
35241         "checkchange" : true
35242     });
35243     if(this.checkHandler){
35244         this.on('checkchange', this.checkHandler, this.scope);
35245     }
35246 };
35247 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35248     /**
35249      * @cfg {String} group
35250      * All check items with the same group name will automatically be grouped into a single-select
35251      * radio button group (defaults to '')
35252      */
35253     /**
35254      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35255      */
35256     itemCls : "x-menu-item x-menu-check-item",
35257     /**
35258      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35259      */
35260     groupClass : "x-menu-group-item",
35261
35262     /**
35263      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35264      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35265      * initialized with checked = true will be rendered as checked.
35266      */
35267     checked: false,
35268
35269     // private
35270     ctype: "Roo.menu.CheckItem",
35271
35272     // private
35273     onRender : function(c){
35274         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35275         if(this.group){
35276             this.el.addClass(this.groupClass);
35277         }
35278         Roo.menu.MenuMgr.registerCheckable(this);
35279         if(this.checked){
35280             this.checked = false;
35281             this.setChecked(true, true);
35282         }
35283     },
35284
35285     // private
35286     destroy : function(){
35287         if(this.rendered){
35288             Roo.menu.MenuMgr.unregisterCheckable(this);
35289         }
35290         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35291     },
35292
35293     /**
35294      * Set the checked state of this item
35295      * @param {Boolean} checked The new checked value
35296      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35297      */
35298     setChecked : function(state, suppressEvent){
35299         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35300             if(this.container){
35301                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35302             }
35303             this.checked = state;
35304             if(suppressEvent !== true){
35305                 this.fireEvent("checkchange", this, state);
35306             }
35307         }
35308     },
35309
35310     // private
35311     handleClick : function(e){
35312        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35313            this.setChecked(!this.checked);
35314        }
35315        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35316     }
35317 });/*
35318  * Based on:
35319  * Ext JS Library 1.1.1
35320  * Copyright(c) 2006-2007, Ext JS, LLC.
35321  *
35322  * Originally Released Under LGPL - original licence link has changed is not relivant.
35323  *
35324  * Fork - LGPL
35325  * <script type="text/javascript">
35326  */
35327  
35328 /**
35329  * @class Roo.menu.DateItem
35330  * @extends Roo.menu.Adapter
35331  * A menu item that wraps the {@link Roo.DatPicker} component.
35332  * @constructor
35333  * Creates a new DateItem
35334  * @param {Object} config Configuration options
35335  */
35336 Roo.menu.DateItem = function(config){
35337     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35338     /** The Roo.DatePicker object @type Roo.DatePicker */
35339     this.picker = this.component;
35340     this.addEvents({select: true});
35341     
35342     this.picker.on("render", function(picker){
35343         picker.getEl().swallowEvent("click");
35344         picker.container.addClass("x-menu-date-item");
35345     });
35346
35347     this.picker.on("select", this.onSelect, this);
35348 };
35349
35350 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35351     // private
35352     onSelect : function(picker, date){
35353         this.fireEvent("select", this, date, picker);
35354         Roo.menu.DateItem.superclass.handleClick.call(this);
35355     }
35356 });/*
35357  * Based on:
35358  * Ext JS Library 1.1.1
35359  * Copyright(c) 2006-2007, Ext JS, LLC.
35360  *
35361  * Originally Released Under LGPL - original licence link has changed is not relivant.
35362  *
35363  * Fork - LGPL
35364  * <script type="text/javascript">
35365  */
35366  
35367 /**
35368  * @class Roo.menu.ColorItem
35369  * @extends Roo.menu.Adapter
35370  * A menu item that wraps the {@link Roo.ColorPalette} component.
35371  * @constructor
35372  * Creates a new ColorItem
35373  * @param {Object} config Configuration options
35374  */
35375 Roo.menu.ColorItem = function(config){
35376     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35377     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35378     this.palette = this.component;
35379     this.relayEvents(this.palette, ["select"]);
35380     if(this.selectHandler){
35381         this.on('select', this.selectHandler, this.scope);
35382     }
35383 };
35384 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35385  * Based on:
35386  * Ext JS Library 1.1.1
35387  * Copyright(c) 2006-2007, Ext JS, LLC.
35388  *
35389  * Originally Released Under LGPL - original licence link has changed is not relivant.
35390  *
35391  * Fork - LGPL
35392  * <script type="text/javascript">
35393  */
35394  
35395
35396 /**
35397  * @class Roo.menu.DateMenu
35398  * @extends Roo.menu.Menu
35399  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35400  * @constructor
35401  * Creates a new DateMenu
35402  * @param {Object} config Configuration options
35403  */
35404 Roo.menu.DateMenu = function(config){
35405     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35406     this.plain = true;
35407     var di = new Roo.menu.DateItem(config);
35408     this.add(di);
35409     /**
35410      * The {@link Roo.DatePicker} instance for this DateMenu
35411      * @type DatePicker
35412      */
35413     this.picker = di.picker;
35414     /**
35415      * @event select
35416      * @param {DatePicker} picker
35417      * @param {Date} date
35418      */
35419     this.relayEvents(di, ["select"]);
35420
35421     this.on('beforeshow', function(){
35422         if(this.picker){
35423             this.picker.hideMonthPicker(true);
35424         }
35425     }, this);
35426 };
35427 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35428     cls:'x-date-menu'
35429 });/*
35430  * Based on:
35431  * Ext JS Library 1.1.1
35432  * Copyright(c) 2006-2007, Ext JS, LLC.
35433  *
35434  * Originally Released Under LGPL - original licence link has changed is not relivant.
35435  *
35436  * Fork - LGPL
35437  * <script type="text/javascript">
35438  */
35439  
35440
35441 /**
35442  * @class Roo.menu.ColorMenu
35443  * @extends Roo.menu.Menu
35444  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35445  * @constructor
35446  * Creates a new ColorMenu
35447  * @param {Object} config Configuration options
35448  */
35449 Roo.menu.ColorMenu = function(config){
35450     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35451     this.plain = true;
35452     var ci = new Roo.menu.ColorItem(config);
35453     this.add(ci);
35454     /**
35455      * The {@link Roo.ColorPalette} instance for this ColorMenu
35456      * @type ColorPalette
35457      */
35458     this.palette = ci.palette;
35459     /**
35460      * @event select
35461      * @param {ColorPalette} palette
35462      * @param {String} color
35463      */
35464     this.relayEvents(ci, ["select"]);
35465 };
35466 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35467  * Based on:
35468  * Ext JS Library 1.1.1
35469  * Copyright(c) 2006-2007, Ext JS, LLC.
35470  *
35471  * Originally Released Under LGPL - original licence link has changed is not relivant.
35472  *
35473  * Fork - LGPL
35474  * <script type="text/javascript">
35475  */
35476  
35477 /**
35478  * @class Roo.form.Field
35479  * @extends Roo.BoxComponent
35480  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35481  * @constructor
35482  * Creates a new Field
35483  * @param {Object} config Configuration options
35484  */
35485 Roo.form.Field = function(config){
35486     Roo.form.Field.superclass.constructor.call(this, config);
35487 };
35488
35489 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35490     /**
35491      * @cfg {String} fieldLabel Label to use when rendering a form.
35492      */
35493        /**
35494      * @cfg {String} qtip Mouse over tip
35495      */
35496      
35497     /**
35498      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35499      */
35500     invalidClass : "x-form-invalid",
35501     /**
35502      * @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")
35503      */
35504     invalidText : "The value in this field is invalid",
35505     /**
35506      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35507      */
35508     focusClass : "x-form-focus",
35509     /**
35510      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35511       automatic validation (defaults to "keyup").
35512      */
35513     validationEvent : "keyup",
35514     /**
35515      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35516      */
35517     validateOnBlur : true,
35518     /**
35519      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35520      */
35521     validationDelay : 250,
35522     /**
35523      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35524      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35525      */
35526     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35527     /**
35528      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35529      */
35530     fieldClass : "x-form-field",
35531     /**
35532      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35533      *<pre>
35534 Value         Description
35535 -----------   ----------------------------------------------------------------------
35536 qtip          Display a quick tip when the user hovers over the field
35537 title         Display a default browser title attribute popup
35538 under         Add a block div beneath the field containing the error text
35539 side          Add an error icon to the right of the field with a popup on hover
35540 [element id]  Add the error text directly to the innerHTML of the specified element
35541 </pre>
35542      */
35543     msgTarget : 'qtip',
35544     /**
35545      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35546      */
35547     msgFx : 'normal',
35548
35549     /**
35550      * @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.
35551      */
35552     readOnly : false,
35553
35554     /**
35555      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35556      */
35557     disabled : false,
35558
35559     /**
35560      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35561      */
35562     inputType : undefined,
35563     
35564     /**
35565      * @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).
35566          */
35567         tabIndex : undefined,
35568         
35569     // private
35570     isFormField : true,
35571
35572     // private
35573     hasFocus : false,
35574     /**
35575      * @property {Roo.Element} fieldEl
35576      * Element Containing the rendered Field (with label etc.)
35577      */
35578     /**
35579      * @cfg {Mixed} value A value to initialize this field with.
35580      */
35581     value : undefined,
35582
35583     /**
35584      * @cfg {String} name The field's HTML name attribute.
35585      */
35586     /**
35587      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35588      */
35589
35590         // private ??
35591         initComponent : function(){
35592         Roo.form.Field.superclass.initComponent.call(this);
35593         this.addEvents({
35594             /**
35595              * @event focus
35596              * Fires when this field receives input focus.
35597              * @param {Roo.form.Field} this
35598              */
35599             focus : true,
35600             /**
35601              * @event blur
35602              * Fires when this field loses input focus.
35603              * @param {Roo.form.Field} this
35604              */
35605             blur : true,
35606             /**
35607              * @event specialkey
35608              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35609              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35610              * @param {Roo.form.Field} this
35611              * @param {Roo.EventObject} e The event object
35612              */
35613             specialkey : true,
35614             /**
35615              * @event change
35616              * Fires just before the field blurs if the field value has changed.
35617              * @param {Roo.form.Field} this
35618              * @param {Mixed} newValue The new value
35619              * @param {Mixed} oldValue The original value
35620              */
35621             change : true,
35622             /**
35623              * @event invalid
35624              * Fires after the field has been marked as invalid.
35625              * @param {Roo.form.Field} this
35626              * @param {String} msg The validation message
35627              */
35628             invalid : true,
35629             /**
35630              * @event valid
35631              * Fires after the field has been validated with no errors.
35632              * @param {Roo.form.Field} this
35633              */
35634             valid : true,
35635              /**
35636              * @event keyup
35637              * Fires after the key up
35638              * @param {Roo.form.Field} this
35639              * @param {Roo.EventObject}  e The event Object
35640              */
35641             keyup : true
35642         });
35643     },
35644
35645     /**
35646      * Returns the name attribute of the field if available
35647      * @return {String} name The field name
35648      */
35649     getName: function(){
35650          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35651     },
35652
35653     // private
35654     onRender : function(ct, position){
35655         Roo.form.Field.superclass.onRender.call(this, ct, position);
35656         if(!this.el){
35657             var cfg = this.getAutoCreate();
35658             if(!cfg.name){
35659                 cfg.name = this.name || this.id;
35660             }
35661             if(this.inputType){
35662                 cfg.type = this.inputType;
35663             }
35664             this.el = ct.createChild(cfg, position);
35665         }
35666         var type = this.el.dom.type;
35667         if(type){
35668             if(type == 'password'){
35669                 type = 'text';
35670             }
35671             this.el.addClass('x-form-'+type);
35672         }
35673         if(this.readOnly){
35674             this.el.dom.readOnly = true;
35675         }
35676         if(this.tabIndex !== undefined){
35677             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35678         }
35679
35680         this.el.addClass([this.fieldClass, this.cls]);
35681         this.initValue();
35682     },
35683
35684     /**
35685      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35686      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35687      * @return {Roo.form.Field} this
35688      */
35689     applyTo : function(target){
35690         this.allowDomMove = false;
35691         this.el = Roo.get(target);
35692         this.render(this.el.dom.parentNode);
35693         return this;
35694     },
35695
35696     // private
35697     initValue : function(){
35698         if(this.value !== undefined){
35699             this.setValue(this.value);
35700         }else if(this.el.dom.value.length > 0){
35701             this.setValue(this.el.dom.value);
35702         }
35703     },
35704
35705     /**
35706      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35707      */
35708     isDirty : function() {
35709         if(this.disabled) {
35710             return false;
35711         }
35712         return String(this.getValue()) !== String(this.originalValue);
35713     },
35714
35715     // private
35716     afterRender : function(){
35717         Roo.form.Field.superclass.afterRender.call(this);
35718         this.initEvents();
35719     },
35720
35721     // private
35722     fireKey : function(e){
35723         //Roo.log('field ' + e.getKey());
35724         if(e.isNavKeyPress()){
35725             this.fireEvent("specialkey", this, e);
35726         }
35727     },
35728
35729     /**
35730      * Resets the current field value to the originally loaded value and clears any validation messages
35731      */
35732     reset : function(){
35733         this.setValue(this.originalValue);
35734         this.clearInvalid();
35735     },
35736
35737     // private
35738     initEvents : function(){
35739         // safari killled keypress - so keydown is now used..
35740         this.el.on("keydown" , this.fireKey,  this);
35741         this.el.on("focus", this.onFocus,  this);
35742         this.el.on("blur", this.onBlur,  this);
35743         this.el.relayEvent('keyup', this);
35744
35745         // reference to original value for reset
35746         this.originalValue = this.getValue();
35747     },
35748
35749     // private
35750     onFocus : function(){
35751         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35752             this.el.addClass(this.focusClass);
35753         }
35754         if(!this.hasFocus){
35755             this.hasFocus = true;
35756             this.startValue = this.getValue();
35757             this.fireEvent("focus", this);
35758         }
35759     },
35760
35761     beforeBlur : Roo.emptyFn,
35762
35763     // private
35764     onBlur : function(){
35765         this.beforeBlur();
35766         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35767             this.el.removeClass(this.focusClass);
35768         }
35769         this.hasFocus = false;
35770         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35771             this.validate();
35772         }
35773         var v = this.getValue();
35774         if(String(v) !== String(this.startValue)){
35775             this.fireEvent('change', this, v, this.startValue);
35776         }
35777         this.fireEvent("blur", this);
35778     },
35779
35780     /**
35781      * Returns whether or not the field value is currently valid
35782      * @param {Boolean} preventMark True to disable marking the field invalid
35783      * @return {Boolean} True if the value is valid, else false
35784      */
35785     isValid : function(preventMark){
35786         if(this.disabled){
35787             return true;
35788         }
35789         var restore = this.preventMark;
35790         this.preventMark = preventMark === true;
35791         var v = this.validateValue(this.processValue(this.getRawValue()));
35792         this.preventMark = restore;
35793         return v;
35794     },
35795
35796     /**
35797      * Validates the field value
35798      * @return {Boolean} True if the value is valid, else false
35799      */
35800     validate : function(){
35801         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35802             this.clearInvalid();
35803             return true;
35804         }
35805         return false;
35806     },
35807
35808     processValue : function(value){
35809         return value;
35810     },
35811
35812     // private
35813     // Subclasses should provide the validation implementation by overriding this
35814     validateValue : function(value){
35815         return true;
35816     },
35817
35818     /**
35819      * Mark this field as invalid
35820      * @param {String} msg The validation message
35821      */
35822     markInvalid : function(msg){
35823         if(!this.rendered || this.preventMark){ // not rendered
35824             return;
35825         }
35826         this.el.addClass(this.invalidClass);
35827         msg = msg || this.invalidText;
35828         switch(this.msgTarget){
35829             case 'qtip':
35830                 this.el.dom.qtip = msg;
35831                 this.el.dom.qclass = 'x-form-invalid-tip';
35832                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35833                     Roo.QuickTips.enable();
35834                 }
35835                 break;
35836             case 'title':
35837                 this.el.dom.title = msg;
35838                 break;
35839             case 'under':
35840                 if(!this.errorEl){
35841                     var elp = this.el.findParent('.x-form-element', 5, true);
35842                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35843                     this.errorEl.setWidth(elp.getWidth(true)-20);
35844                 }
35845                 this.errorEl.update(msg);
35846                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35847                 break;
35848             case 'side':
35849                 if(!this.errorIcon){
35850                     var elp = this.el.findParent('.x-form-element', 5, true);
35851                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35852                 }
35853                 this.alignErrorIcon();
35854                 this.errorIcon.dom.qtip = msg;
35855                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35856                 this.errorIcon.show();
35857                 this.on('resize', this.alignErrorIcon, this);
35858                 break;
35859             default:
35860                 var t = Roo.getDom(this.msgTarget);
35861                 t.innerHTML = msg;
35862                 t.style.display = this.msgDisplay;
35863                 break;
35864         }
35865         this.fireEvent('invalid', this, msg);
35866     },
35867
35868     // private
35869     alignErrorIcon : function(){
35870         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35871     },
35872
35873     /**
35874      * Clear any invalid styles/messages for this field
35875      */
35876     clearInvalid : function(){
35877         if(!this.rendered || this.preventMark){ // not rendered
35878             return;
35879         }
35880         this.el.removeClass(this.invalidClass);
35881         switch(this.msgTarget){
35882             case 'qtip':
35883                 this.el.dom.qtip = '';
35884                 break;
35885             case 'title':
35886                 this.el.dom.title = '';
35887                 break;
35888             case 'under':
35889                 if(this.errorEl){
35890                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35891                 }
35892                 break;
35893             case 'side':
35894                 if(this.errorIcon){
35895                     this.errorIcon.dom.qtip = '';
35896                     this.errorIcon.hide();
35897                     this.un('resize', this.alignErrorIcon, this);
35898                 }
35899                 break;
35900             default:
35901                 var t = Roo.getDom(this.msgTarget);
35902                 t.innerHTML = '';
35903                 t.style.display = 'none';
35904                 break;
35905         }
35906         this.fireEvent('valid', this);
35907     },
35908
35909     /**
35910      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35911      * @return {Mixed} value The field value
35912      */
35913     getRawValue : function(){
35914         var v = this.el.getValue();
35915         if(v === this.emptyText){
35916             v = '';
35917         }
35918         return v;
35919     },
35920
35921     /**
35922      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35923      * @return {Mixed} value The field value
35924      */
35925     getValue : function(){
35926         var v = this.el.getValue();
35927         if(v === this.emptyText || v === undefined){
35928             v = '';
35929         }
35930         return v;
35931     },
35932
35933     /**
35934      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35935      * @param {Mixed} value The value to set
35936      */
35937     setRawValue : function(v){
35938         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35939     },
35940
35941     /**
35942      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35943      * @param {Mixed} value The value to set
35944      */
35945     setValue : function(v){
35946         this.value = v;
35947         if(this.rendered){
35948             this.el.dom.value = (v === null || v === undefined ? '' : v);
35949              this.validate();
35950         }
35951     },
35952
35953     adjustSize : function(w, h){
35954         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35955         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35956         return s;
35957     },
35958
35959     adjustWidth : function(tag, w){
35960         tag = tag.toLowerCase();
35961         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35962             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35963                 if(tag == 'input'){
35964                     return w + 2;
35965                 }
35966                 if(tag = 'textarea'){
35967                     return w-2;
35968                 }
35969             }else if(Roo.isOpera){
35970                 if(tag == 'input'){
35971                     return w + 2;
35972                 }
35973                 if(tag = 'textarea'){
35974                     return w-2;
35975                 }
35976             }
35977         }
35978         return w;
35979     }
35980 });
35981
35982
35983 // anything other than normal should be considered experimental
35984 Roo.form.Field.msgFx = {
35985     normal : {
35986         show: function(msgEl, f){
35987             msgEl.setDisplayed('block');
35988         },
35989
35990         hide : function(msgEl, f){
35991             msgEl.setDisplayed(false).update('');
35992         }
35993     },
35994
35995     slide : {
35996         show: function(msgEl, f){
35997             msgEl.slideIn('t', {stopFx:true});
35998         },
35999
36000         hide : function(msgEl, f){
36001             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36002         }
36003     },
36004
36005     slideRight : {
36006         show: function(msgEl, f){
36007             msgEl.fixDisplay();
36008             msgEl.alignTo(f.el, 'tl-tr');
36009             msgEl.slideIn('l', {stopFx:true});
36010         },
36011
36012         hide : function(msgEl, f){
36013             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36014         }
36015     }
36016 };/*
36017  * Based on:
36018  * Ext JS Library 1.1.1
36019  * Copyright(c) 2006-2007, Ext JS, LLC.
36020  *
36021  * Originally Released Under LGPL - original licence link has changed is not relivant.
36022  *
36023  * Fork - LGPL
36024  * <script type="text/javascript">
36025  */
36026  
36027
36028 /**
36029  * @class Roo.form.TextField
36030  * @extends Roo.form.Field
36031  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36032  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36033  * @constructor
36034  * Creates a new TextField
36035  * @param {Object} config Configuration options
36036  */
36037 Roo.form.TextField = function(config){
36038     Roo.form.TextField.superclass.constructor.call(this, config);
36039     this.addEvents({
36040         /**
36041          * @event autosize
36042          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36043          * according to the default logic, but this event provides a hook for the developer to apply additional
36044          * logic at runtime to resize the field if needed.
36045              * @param {Roo.form.Field} this This text field
36046              * @param {Number} width The new field width
36047              */
36048         autosize : true
36049     });
36050 };
36051
36052 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36053     /**
36054      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36055      */
36056     grow : false,
36057     /**
36058      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36059      */
36060     growMin : 30,
36061     /**
36062      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36063      */
36064     growMax : 800,
36065     /**
36066      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36067      */
36068     vtype : null,
36069     /**
36070      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36071      */
36072     maskRe : null,
36073     /**
36074      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36075      */
36076     disableKeyFilter : false,
36077     /**
36078      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36079      */
36080     allowBlank : true,
36081     /**
36082      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36083      */
36084     minLength : 0,
36085     /**
36086      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36087      */
36088     maxLength : Number.MAX_VALUE,
36089     /**
36090      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36091      */
36092     minLengthText : "The minimum length for this field is {0}",
36093     /**
36094      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36095      */
36096     maxLengthText : "The maximum length for this field is {0}",
36097     /**
36098      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36099      */
36100     selectOnFocus : false,
36101     /**
36102      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36103      */
36104     blankText : "This field is required",
36105     /**
36106      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36107      * If available, this function will be called only after the basic validators all return true, and will be passed the
36108      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36109      */
36110     validator : null,
36111     /**
36112      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36113      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36114      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36115      */
36116     regex : null,
36117     /**
36118      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36119      */
36120     regexText : "",
36121     /**
36122      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36123      */
36124     emptyText : null,
36125     /**
36126      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36127      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36128      */
36129     emptyClass : 'x-form-empty-field',
36130
36131     // private
36132     initEvents : function(){
36133         Roo.form.TextField.superclass.initEvents.call(this);
36134         if(this.validationEvent == 'keyup'){
36135             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36136             this.el.on('keyup', this.filterValidation, this);
36137         }
36138         else if(this.validationEvent !== false){
36139             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36140         }
36141         if(this.selectOnFocus || this.emptyText){
36142             this.on("focus", this.preFocus, this);
36143             if(this.emptyText){
36144                 this.on('blur', this.postBlur, this);
36145                 this.applyEmptyText();
36146             }
36147         }
36148         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36149             this.el.on("keypress", this.filterKeys, this);
36150         }
36151         if(this.grow){
36152             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36153             this.el.on("click", this.autoSize,  this);
36154         }
36155     },
36156
36157     processValue : function(value){
36158         if(this.stripCharsRe){
36159             var newValue = value.replace(this.stripCharsRe, '');
36160             if(newValue !== value){
36161                 this.setRawValue(newValue);
36162                 return newValue;
36163             }
36164         }
36165         return value;
36166     },
36167
36168     filterValidation : function(e){
36169         if(!e.isNavKeyPress()){
36170             this.validationTask.delay(this.validationDelay);
36171         }
36172     },
36173
36174     // private
36175     onKeyUp : function(e){
36176         if(!e.isNavKeyPress()){
36177             this.autoSize();
36178         }
36179     },
36180
36181     /**
36182      * Resets the current field value to the originally-loaded value and clears any validation messages.
36183      * Also adds emptyText and emptyClass if the original value was blank.
36184      */
36185     reset : function(){
36186         Roo.form.TextField.superclass.reset.call(this);
36187         this.applyEmptyText();
36188     },
36189
36190     applyEmptyText : function(){
36191         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36192             this.setRawValue(this.emptyText);
36193             this.el.addClass(this.emptyClass);
36194         }
36195     },
36196
36197     // private
36198     preFocus : function(){
36199         if(this.emptyText){
36200             if(this.el.dom.value == this.emptyText){
36201                 this.setRawValue('');
36202             }
36203             this.el.removeClass(this.emptyClass);
36204         }
36205         if(this.selectOnFocus){
36206             this.el.dom.select();
36207         }
36208     },
36209
36210     // private
36211     postBlur : function(){
36212         this.applyEmptyText();
36213     },
36214
36215     // private
36216     filterKeys : function(e){
36217         var k = e.getKey();
36218         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36219             return;
36220         }
36221         var c = e.getCharCode(), cc = String.fromCharCode(c);
36222         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36223             return;
36224         }
36225         if(!this.maskRe.test(cc)){
36226             e.stopEvent();
36227         }
36228     },
36229
36230     setValue : function(v){
36231         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36232             this.el.removeClass(this.emptyClass);
36233         }
36234         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36235         this.applyEmptyText();
36236         this.autoSize();
36237     },
36238
36239     /**
36240      * Validates a value according to the field's validation rules and marks the field as invalid
36241      * if the validation fails
36242      * @param {Mixed} value The value to validate
36243      * @return {Boolean} True if the value is valid, else false
36244      */
36245     validateValue : function(value){
36246         if(value.length < 1 || value === this.emptyText){ // if it's blank
36247              if(this.allowBlank){
36248                 this.clearInvalid();
36249                 return true;
36250              }else{
36251                 this.markInvalid(this.blankText);
36252                 return false;
36253              }
36254         }
36255         if(value.length < this.minLength){
36256             this.markInvalid(String.format(this.minLengthText, this.minLength));
36257             return false;
36258         }
36259         if(value.length > this.maxLength){
36260             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36261             return false;
36262         }
36263         if(this.vtype){
36264             var vt = Roo.form.VTypes;
36265             if(!vt[this.vtype](value, this)){
36266                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36267                 return false;
36268             }
36269         }
36270         if(typeof this.validator == "function"){
36271             var msg = this.validator(value);
36272             if(msg !== true){
36273                 this.markInvalid(msg);
36274                 return false;
36275             }
36276         }
36277         if(this.regex && !this.regex.test(value)){
36278             this.markInvalid(this.regexText);
36279             return false;
36280         }
36281         return true;
36282     },
36283
36284     /**
36285      * Selects text in this field
36286      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36287      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36288      */
36289     selectText : function(start, end){
36290         var v = this.getRawValue();
36291         if(v.length > 0){
36292             start = start === undefined ? 0 : start;
36293             end = end === undefined ? v.length : end;
36294             var d = this.el.dom;
36295             if(d.setSelectionRange){
36296                 d.setSelectionRange(start, end);
36297             }else if(d.createTextRange){
36298                 var range = d.createTextRange();
36299                 range.moveStart("character", start);
36300                 range.moveEnd("character", v.length-end);
36301                 range.select();
36302             }
36303         }
36304     },
36305
36306     /**
36307      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36308      * This only takes effect if grow = true, and fires the autosize event.
36309      */
36310     autoSize : function(){
36311         if(!this.grow || !this.rendered){
36312             return;
36313         }
36314         if(!this.metrics){
36315             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36316         }
36317         var el = this.el;
36318         var v = el.dom.value;
36319         var d = document.createElement('div');
36320         d.appendChild(document.createTextNode(v));
36321         v = d.innerHTML;
36322         d = null;
36323         v += "&#160;";
36324         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36325         this.el.setWidth(w);
36326         this.fireEvent("autosize", this, w);
36327     }
36328 });/*
36329  * Based on:
36330  * Ext JS Library 1.1.1
36331  * Copyright(c) 2006-2007, Ext JS, LLC.
36332  *
36333  * Originally Released Under LGPL - original licence link has changed is not relivant.
36334  *
36335  * Fork - LGPL
36336  * <script type="text/javascript">
36337  */
36338  
36339 /**
36340  * @class Roo.form.Hidden
36341  * @extends Roo.form.TextField
36342  * Simple Hidden element used on forms 
36343  * 
36344  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36345  * 
36346  * @constructor
36347  * Creates a new Hidden form element.
36348  * @param {Object} config Configuration options
36349  */
36350
36351
36352
36353 // easy hidden field...
36354 Roo.form.Hidden = function(config){
36355     Roo.form.Hidden.superclass.constructor.call(this, config);
36356 };
36357   
36358 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36359     fieldLabel:      '',
36360     inputType:      'hidden',
36361     width:          50,
36362     allowBlank:     true,
36363     labelSeparator: '',
36364     hidden:         true,
36365     itemCls :       'x-form-item-display-none'
36366
36367
36368 });
36369
36370
36371 /*
36372  * Based on:
36373  * Ext JS Library 1.1.1
36374  * Copyright(c) 2006-2007, Ext JS, LLC.
36375  *
36376  * Originally Released Under LGPL - original licence link has changed is not relivant.
36377  *
36378  * Fork - LGPL
36379  * <script type="text/javascript">
36380  */
36381  
36382 /**
36383  * @class Roo.form.TriggerField
36384  * @extends Roo.form.TextField
36385  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36386  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36387  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36388  * for which you can provide a custom implementation.  For example:
36389  * <pre><code>
36390 var trigger = new Roo.form.TriggerField();
36391 trigger.onTriggerClick = myTriggerFn;
36392 trigger.applyTo('my-field');
36393 </code></pre>
36394  *
36395  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36396  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36397  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36398  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36399  * @constructor
36400  * Create a new TriggerField.
36401  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36402  * to the base TextField)
36403  */
36404 Roo.form.TriggerField = function(config){
36405     this.mimicing = false;
36406     Roo.form.TriggerField.superclass.constructor.call(this, config);
36407 };
36408
36409 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36410     /**
36411      * @cfg {String} triggerClass A CSS class to apply to the trigger
36412      */
36413     /**
36414      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36415      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36416      */
36417     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36418     /**
36419      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36420      */
36421     hideTrigger:false,
36422
36423     /** @cfg {Boolean} grow @hide */
36424     /** @cfg {Number} growMin @hide */
36425     /** @cfg {Number} growMax @hide */
36426
36427     /**
36428      * @hide 
36429      * @method
36430      */
36431     autoSize: Roo.emptyFn,
36432     // private
36433     monitorTab : true,
36434     // private
36435     deferHeight : true,
36436
36437     
36438     actionMode : 'wrap',
36439     // private
36440     onResize : function(w, h){
36441         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36442         if(typeof w == 'number'){
36443             var x = w - this.trigger.getWidth();
36444             this.el.setWidth(this.adjustWidth('input', x));
36445             this.trigger.setStyle('left', x+'px');
36446         }
36447     },
36448
36449     // private
36450     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36451
36452     // private
36453     getResizeEl : function(){
36454         return this.wrap;
36455     },
36456
36457     // private
36458     getPositionEl : function(){
36459         return this.wrap;
36460     },
36461
36462     // private
36463     alignErrorIcon : function(){
36464         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36465     },
36466
36467     // private
36468     onRender : function(ct, position){
36469         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36470         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36471         this.trigger = this.wrap.createChild(this.triggerConfig ||
36472                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36473         if(this.hideTrigger){
36474             this.trigger.setDisplayed(false);
36475         }
36476         this.initTrigger();
36477         if(!this.width){
36478             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36479         }
36480     },
36481
36482     // private
36483     initTrigger : function(){
36484         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36485         this.trigger.addClassOnOver('x-form-trigger-over');
36486         this.trigger.addClassOnClick('x-form-trigger-click');
36487     },
36488
36489     // private
36490     onDestroy : function(){
36491         if(this.trigger){
36492             this.trigger.removeAllListeners();
36493             this.trigger.remove();
36494         }
36495         if(this.wrap){
36496             this.wrap.remove();
36497         }
36498         Roo.form.TriggerField.superclass.onDestroy.call(this);
36499     },
36500
36501     // private
36502     onFocus : function(){
36503         Roo.form.TriggerField.superclass.onFocus.call(this);
36504         if(!this.mimicing){
36505             this.wrap.addClass('x-trigger-wrap-focus');
36506             this.mimicing = true;
36507             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36508             if(this.monitorTab){
36509                 this.el.on("keydown", this.checkTab, this);
36510             }
36511         }
36512     },
36513
36514     // private
36515     checkTab : function(e){
36516         if(e.getKey() == e.TAB){
36517             this.triggerBlur();
36518         }
36519     },
36520
36521     // private
36522     onBlur : function(){
36523         // do nothing
36524     },
36525
36526     // private
36527     mimicBlur : function(e, t){
36528         if(!this.wrap.contains(t) && this.validateBlur()){
36529             this.triggerBlur();
36530         }
36531     },
36532
36533     // private
36534     triggerBlur : function(){
36535         this.mimicing = false;
36536         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36537         if(this.monitorTab){
36538             this.el.un("keydown", this.checkTab, this);
36539         }
36540         this.wrap.removeClass('x-trigger-wrap-focus');
36541         Roo.form.TriggerField.superclass.onBlur.call(this);
36542     },
36543
36544     // private
36545     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36546     validateBlur : function(e, t){
36547         return true;
36548     },
36549
36550     // private
36551     onDisable : function(){
36552         Roo.form.TriggerField.superclass.onDisable.call(this);
36553         if(this.wrap){
36554             this.wrap.addClass('x-item-disabled');
36555         }
36556     },
36557
36558     // private
36559     onEnable : function(){
36560         Roo.form.TriggerField.superclass.onEnable.call(this);
36561         if(this.wrap){
36562             this.wrap.removeClass('x-item-disabled');
36563         }
36564     },
36565
36566     // private
36567     onShow : function(){
36568         var ae = this.getActionEl();
36569         
36570         if(ae){
36571             ae.dom.style.display = '';
36572             ae.dom.style.visibility = 'visible';
36573         }
36574     },
36575
36576     // private
36577     
36578     onHide : function(){
36579         var ae = this.getActionEl();
36580         ae.dom.style.display = 'none';
36581     },
36582
36583     /**
36584      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36585      * by an implementing function.
36586      * @method
36587      * @param {EventObject} e
36588      */
36589     onTriggerClick : Roo.emptyFn
36590 });
36591
36592 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36593 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36594 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36595 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36596     initComponent : function(){
36597         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36598
36599         this.triggerConfig = {
36600             tag:'span', cls:'x-form-twin-triggers', cn:[
36601             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36602             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36603         ]};
36604     },
36605
36606     getTrigger : function(index){
36607         return this.triggers[index];
36608     },
36609
36610     initTrigger : function(){
36611         var ts = this.trigger.select('.x-form-trigger', true);
36612         this.wrap.setStyle('overflow', 'hidden');
36613         var triggerField = this;
36614         ts.each(function(t, all, index){
36615             t.hide = function(){
36616                 var w = triggerField.wrap.getWidth();
36617                 this.dom.style.display = 'none';
36618                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36619             };
36620             t.show = function(){
36621                 var w = triggerField.wrap.getWidth();
36622                 this.dom.style.display = '';
36623                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36624             };
36625             var triggerIndex = 'Trigger'+(index+1);
36626
36627             if(this['hide'+triggerIndex]){
36628                 t.dom.style.display = 'none';
36629             }
36630             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36631             t.addClassOnOver('x-form-trigger-over');
36632             t.addClassOnClick('x-form-trigger-click');
36633         }, this);
36634         this.triggers = ts.elements;
36635     },
36636
36637     onTrigger1Click : Roo.emptyFn,
36638     onTrigger2Click : Roo.emptyFn
36639 });/*
36640  * Based on:
36641  * Ext JS Library 1.1.1
36642  * Copyright(c) 2006-2007, Ext JS, LLC.
36643  *
36644  * Originally Released Under LGPL - original licence link has changed is not relivant.
36645  *
36646  * Fork - LGPL
36647  * <script type="text/javascript">
36648  */
36649  
36650 /**
36651  * @class Roo.form.TextArea
36652  * @extends Roo.form.TextField
36653  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36654  * support for auto-sizing.
36655  * @constructor
36656  * Creates a new TextArea
36657  * @param {Object} config Configuration options
36658  */
36659 Roo.form.TextArea = function(config){
36660     Roo.form.TextArea.superclass.constructor.call(this, config);
36661     // these are provided exchanges for backwards compat
36662     // minHeight/maxHeight were replaced by growMin/growMax to be
36663     // compatible with TextField growing config values
36664     if(this.minHeight !== undefined){
36665         this.growMin = this.minHeight;
36666     }
36667     if(this.maxHeight !== undefined){
36668         this.growMax = this.maxHeight;
36669     }
36670 };
36671
36672 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36673     /**
36674      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36675      */
36676     growMin : 60,
36677     /**
36678      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36679      */
36680     growMax: 1000,
36681     /**
36682      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36683      * in the field (equivalent to setting overflow: hidden, defaults to false)
36684      */
36685     preventScrollbars: false,
36686     /**
36687      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36688      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36689      */
36690
36691     // private
36692     onRender : function(ct, position){
36693         if(!this.el){
36694             this.defaultAutoCreate = {
36695                 tag: "textarea",
36696                 style:"width:300px;height:60px;",
36697                 autocomplete: "off"
36698             };
36699         }
36700         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36701         if(this.grow){
36702             this.textSizeEl = Roo.DomHelper.append(document.body, {
36703                 tag: "pre", cls: "x-form-grow-sizer"
36704             });
36705             if(this.preventScrollbars){
36706                 this.el.setStyle("overflow", "hidden");
36707             }
36708             this.el.setHeight(this.growMin);
36709         }
36710     },
36711
36712     onDestroy : function(){
36713         if(this.textSizeEl){
36714             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36715         }
36716         Roo.form.TextArea.superclass.onDestroy.call(this);
36717     },
36718
36719     // private
36720     onKeyUp : function(e){
36721         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36722             this.autoSize();
36723         }
36724     },
36725
36726     /**
36727      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36728      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36729      */
36730     autoSize : function(){
36731         if(!this.grow || !this.textSizeEl){
36732             return;
36733         }
36734         var el = this.el;
36735         var v = el.dom.value;
36736         var ts = this.textSizeEl;
36737
36738         ts.innerHTML = '';
36739         ts.appendChild(document.createTextNode(v));
36740         v = ts.innerHTML;
36741
36742         Roo.fly(ts).setWidth(this.el.getWidth());
36743         if(v.length < 1){
36744             v = "&#160;&#160;";
36745         }else{
36746             if(Roo.isIE){
36747                 v = v.replace(/\n/g, '<p>&#160;</p>');
36748             }
36749             v += "&#160;\n&#160;";
36750         }
36751         ts.innerHTML = v;
36752         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36753         if(h != this.lastHeight){
36754             this.lastHeight = h;
36755             this.el.setHeight(h);
36756             this.fireEvent("autosize", this, h);
36757         }
36758     }
36759 });/*
36760  * Based on:
36761  * Ext JS Library 1.1.1
36762  * Copyright(c) 2006-2007, Ext JS, LLC.
36763  *
36764  * Originally Released Under LGPL - original licence link has changed is not relivant.
36765  *
36766  * Fork - LGPL
36767  * <script type="text/javascript">
36768  */
36769  
36770
36771 /**
36772  * @class Roo.form.NumberField
36773  * @extends Roo.form.TextField
36774  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36775  * @constructor
36776  * Creates a new NumberField
36777  * @param {Object} config Configuration options
36778  */
36779 Roo.form.NumberField = function(config){
36780     Roo.form.NumberField.superclass.constructor.call(this, config);
36781 };
36782
36783 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36784     /**
36785      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36786      */
36787     fieldClass: "x-form-field x-form-num-field",
36788     /**
36789      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36790      */
36791     allowDecimals : true,
36792     /**
36793      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36794      */
36795     decimalSeparator : ".",
36796     /**
36797      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36798      */
36799     decimalPrecision : 2,
36800     /**
36801      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36802      */
36803     allowNegative : true,
36804     /**
36805      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36806      */
36807     minValue : Number.NEGATIVE_INFINITY,
36808     /**
36809      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36810      */
36811     maxValue : Number.MAX_VALUE,
36812     /**
36813      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36814      */
36815     minText : "The minimum value for this field is {0}",
36816     /**
36817      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36818      */
36819     maxText : "The maximum value for this field is {0}",
36820     /**
36821      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36822      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36823      */
36824     nanText : "{0} is not a valid number",
36825
36826     // private
36827     initEvents : function(){
36828         Roo.form.NumberField.superclass.initEvents.call(this);
36829         var allowed = "0123456789";
36830         if(this.allowDecimals){
36831             allowed += this.decimalSeparator;
36832         }
36833         if(this.allowNegative){
36834             allowed += "-";
36835         }
36836         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36837         var keyPress = function(e){
36838             var k = e.getKey();
36839             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36840                 return;
36841             }
36842             var c = e.getCharCode();
36843             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36844                 e.stopEvent();
36845             }
36846         };
36847         this.el.on("keypress", keyPress, this);
36848     },
36849
36850     // private
36851     validateValue : function(value){
36852         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36853             return false;
36854         }
36855         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36856              return true;
36857         }
36858         var num = this.parseValue(value);
36859         if(isNaN(num)){
36860             this.markInvalid(String.format(this.nanText, value));
36861             return false;
36862         }
36863         if(num < this.minValue){
36864             this.markInvalid(String.format(this.minText, this.minValue));
36865             return false;
36866         }
36867         if(num > this.maxValue){
36868             this.markInvalid(String.format(this.maxText, this.maxValue));
36869             return false;
36870         }
36871         return true;
36872     },
36873
36874     getValue : function(){
36875         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36876     },
36877
36878     // private
36879     parseValue : function(value){
36880         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36881         return isNaN(value) ? '' : value;
36882     },
36883
36884     // private
36885     fixPrecision : function(value){
36886         var nan = isNaN(value);
36887         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36888             return nan ? '' : value;
36889         }
36890         return parseFloat(value).toFixed(this.decimalPrecision);
36891     },
36892
36893     setValue : function(v){
36894         v = this.fixPrecision(v);
36895         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36896     },
36897
36898     // private
36899     decimalPrecisionFcn : function(v){
36900         return Math.floor(v);
36901     },
36902
36903     beforeBlur : function(){
36904         var v = this.parseValue(this.getRawValue());
36905         if(v){
36906             this.setValue(v);
36907         }
36908     }
36909 });/*
36910  * Based on:
36911  * Ext JS Library 1.1.1
36912  * Copyright(c) 2006-2007, Ext JS, LLC.
36913  *
36914  * Originally Released Under LGPL - original licence link has changed is not relivant.
36915  *
36916  * Fork - LGPL
36917  * <script type="text/javascript">
36918  */
36919  
36920 /**
36921  * @class Roo.form.DateField
36922  * @extends Roo.form.TriggerField
36923  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36924 * @constructor
36925 * Create a new DateField
36926 * @param {Object} config
36927  */
36928 Roo.form.DateField = function(config){
36929     Roo.form.DateField.superclass.constructor.call(this, config);
36930     
36931       this.addEvents({
36932          
36933         /**
36934          * @event select
36935          * Fires when a date is selected
36936              * @param {Roo.form.DateField} combo This combo box
36937              * @param {Date} date The date selected
36938              */
36939         'select' : true
36940          
36941     });
36942     
36943     
36944     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36945     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36946     this.ddMatch = null;
36947     if(this.disabledDates){
36948         var dd = this.disabledDates;
36949         var re = "(?:";
36950         for(var i = 0; i < dd.length; i++){
36951             re += dd[i];
36952             if(i != dd.length-1) re += "|";
36953         }
36954         this.ddMatch = new RegExp(re + ")");
36955     }
36956 };
36957
36958 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36959     /**
36960      * @cfg {String} format
36961      * The default date format string which can be overriden for localization support.  The format must be
36962      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36963      */
36964     format : "m/d/y",
36965     /**
36966      * @cfg {String} altFormats
36967      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36968      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36969      */
36970     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36971     /**
36972      * @cfg {Array} disabledDays
36973      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36974      */
36975     disabledDays : null,
36976     /**
36977      * @cfg {String} disabledDaysText
36978      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36979      */
36980     disabledDaysText : "Disabled",
36981     /**
36982      * @cfg {Array} disabledDates
36983      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36984      * expression so they are very powerful. Some examples:
36985      * <ul>
36986      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36987      * <li>["03/08", "09/16"] would disable those days for every year</li>
36988      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36989      * <li>["03/../2006"] would disable every day in March 2006</li>
36990      * <li>["^03"] would disable every day in every March</li>
36991      * </ul>
36992      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36993      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36994      */
36995     disabledDates : null,
36996     /**
36997      * @cfg {String} disabledDatesText
36998      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36999      */
37000     disabledDatesText : "Disabled",
37001     /**
37002      * @cfg {Date/String} minValue
37003      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37004      * valid format (defaults to null).
37005      */
37006     minValue : null,
37007     /**
37008      * @cfg {Date/String} maxValue
37009      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37010      * valid format (defaults to null).
37011      */
37012     maxValue : null,
37013     /**
37014      * @cfg {String} minText
37015      * The error text to display when the date in the cell is before minValue (defaults to
37016      * 'The date in this field must be after {minValue}').
37017      */
37018     minText : "The date in this field must be equal to or after {0}",
37019     /**
37020      * @cfg {String} maxText
37021      * The error text to display when the date in the cell is after maxValue (defaults to
37022      * 'The date in this field must be before {maxValue}').
37023      */
37024     maxText : "The date in this field must be equal to or before {0}",
37025     /**
37026      * @cfg {String} invalidText
37027      * The error text to display when the date in the field is invalid (defaults to
37028      * '{value} is not a valid date - it must be in the format {format}').
37029      */
37030     invalidText : "{0} is not a valid date - it must be in the format {1}",
37031     /**
37032      * @cfg {String} triggerClass
37033      * An additional CSS class used to style the trigger button.  The trigger will always get the
37034      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37035      * which displays a calendar icon).
37036      */
37037     triggerClass : 'x-form-date-trigger',
37038     
37039
37040     /**
37041      * @cfg {bool} useIso
37042      * if enabled, then the date field will use a hidden field to store the 
37043      * real value as iso formated date. default (false)
37044      */ 
37045     useIso : false,
37046     /**
37047      * @cfg {String/Object} autoCreate
37048      * A DomHelper element spec, or true for a default element spec (defaults to
37049      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37050      */ 
37051     // private
37052     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37053     
37054     // private
37055     hiddenField: false,
37056     
37057     onRender : function(ct, position)
37058     {
37059         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37060         if (this.useIso) {
37061             this.el.dom.removeAttribute('name'); 
37062             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37063                     'before', true);
37064             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37065             // prevent input submission
37066             this.hiddenName = this.name;
37067         }
37068             
37069             
37070     },
37071     
37072     // private
37073     validateValue : function(value)
37074     {
37075         value = this.formatDate(value);
37076         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37077             return false;
37078         }
37079         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37080              return true;
37081         }
37082         var svalue = value;
37083         value = this.parseDate(value);
37084         if(!value){
37085             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37086             return false;
37087         }
37088         var time = value.getTime();
37089         if(this.minValue && time < this.minValue.getTime()){
37090             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37091             return false;
37092         }
37093         if(this.maxValue && time > this.maxValue.getTime()){
37094             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37095             return false;
37096         }
37097         if(this.disabledDays){
37098             var day = value.getDay();
37099             for(var i = 0; i < this.disabledDays.length; i++) {
37100                 if(day === this.disabledDays[i]){
37101                     this.markInvalid(this.disabledDaysText);
37102                     return false;
37103                 }
37104             }
37105         }
37106         var fvalue = this.formatDate(value);
37107         if(this.ddMatch && this.ddMatch.test(fvalue)){
37108             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37109             return false;
37110         }
37111         return true;
37112     },
37113
37114     // private
37115     // Provides logic to override the default TriggerField.validateBlur which just returns true
37116     validateBlur : function(){
37117         return !this.menu || !this.menu.isVisible();
37118     },
37119
37120     /**
37121      * Returns the current date value of the date field.
37122      * @return {Date} The date value
37123      */
37124     getValue : function(){
37125         
37126         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37127     },
37128
37129     /**
37130      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37131      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37132      * (the default format used is "m/d/y").
37133      * <br />Usage:
37134      * <pre><code>
37135 //All of these calls set the same date value (May 4, 2006)
37136
37137 //Pass a date object:
37138 var dt = new Date('5/4/06');
37139 dateField.setValue(dt);
37140
37141 //Pass a date string (default format):
37142 dateField.setValue('5/4/06');
37143
37144 //Pass a date string (custom format):
37145 dateField.format = 'Y-m-d';
37146 dateField.setValue('2006-5-4');
37147 </code></pre>
37148      * @param {String/Date} date The date or valid date string
37149      */
37150     setValue : function(date){
37151         if (this.hiddenField) {
37152             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37153         }
37154         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37155     },
37156
37157     // private
37158     parseDate : function(value){
37159         if(!value || value instanceof Date){
37160             return value;
37161         }
37162         var v = Date.parseDate(value, this.format);
37163         if(!v && this.altFormats){
37164             if(!this.altFormatsArray){
37165                 this.altFormatsArray = this.altFormats.split("|");
37166             }
37167             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37168                 v = Date.parseDate(value, this.altFormatsArray[i]);
37169             }
37170         }
37171         return v;
37172     },
37173
37174     // private
37175     formatDate : function(date, fmt){
37176         return (!date || !(date instanceof Date)) ?
37177                date : date.dateFormat(fmt || this.format);
37178     },
37179
37180     // private
37181     menuListeners : {
37182         select: function(m, d){
37183             this.setValue(d);
37184             this.fireEvent('select', this, d);
37185         },
37186         show : function(){ // retain focus styling
37187             this.onFocus();
37188         },
37189         hide : function(){
37190             this.focus.defer(10, this);
37191             var ml = this.menuListeners;
37192             this.menu.un("select", ml.select,  this);
37193             this.menu.un("show", ml.show,  this);
37194             this.menu.un("hide", ml.hide,  this);
37195         }
37196     },
37197
37198     // private
37199     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37200     onTriggerClick : function(){
37201         if(this.disabled){
37202             return;
37203         }
37204         if(this.menu == null){
37205             this.menu = new Roo.menu.DateMenu();
37206         }
37207         Roo.apply(this.menu.picker,  {
37208             showClear: this.allowBlank,
37209             minDate : this.minValue,
37210             maxDate : this.maxValue,
37211             disabledDatesRE : this.ddMatch,
37212             disabledDatesText : this.disabledDatesText,
37213             disabledDays : this.disabledDays,
37214             disabledDaysText : this.disabledDaysText,
37215             format : this.format,
37216             minText : String.format(this.minText, this.formatDate(this.minValue)),
37217             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37218         });
37219         this.menu.on(Roo.apply({}, this.menuListeners, {
37220             scope:this
37221         }));
37222         this.menu.picker.setValue(this.getValue() || new Date());
37223         this.menu.show(this.el, "tl-bl?");
37224     },
37225
37226     beforeBlur : function(){
37227         var v = this.parseDate(this.getRawValue());
37228         if(v){
37229             this.setValue(v);
37230         }
37231     }
37232
37233     /** @cfg {Boolean} grow @hide */
37234     /** @cfg {Number} growMin @hide */
37235     /** @cfg {Number} growMax @hide */
37236     /**
37237      * @hide
37238      * @method autoSize
37239      */
37240 });/*
37241  * Based on:
37242  * Ext JS Library 1.1.1
37243  * Copyright(c) 2006-2007, Ext JS, LLC.
37244  *
37245  * Originally Released Under LGPL - original licence link has changed is not relivant.
37246  *
37247  * Fork - LGPL
37248  * <script type="text/javascript">
37249  */
37250  
37251
37252 /**
37253  * @class Roo.form.ComboBox
37254  * @extends Roo.form.TriggerField
37255  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37256  * @constructor
37257  * Create a new ComboBox.
37258  * @param {Object} config Configuration options
37259  */
37260 Roo.form.ComboBox = function(config){
37261     Roo.form.ComboBox.superclass.constructor.call(this, config);
37262     this.addEvents({
37263         /**
37264          * @event expand
37265          * Fires when the dropdown list is expanded
37266              * @param {Roo.form.ComboBox} combo This combo box
37267              */
37268         'expand' : true,
37269         /**
37270          * @event collapse
37271          * Fires when the dropdown list is collapsed
37272              * @param {Roo.form.ComboBox} combo This combo box
37273              */
37274         'collapse' : true,
37275         /**
37276          * @event beforeselect
37277          * Fires before a list item is selected. Return false to cancel the selection.
37278              * @param {Roo.form.ComboBox} combo This combo box
37279              * @param {Roo.data.Record} record The data record returned from the underlying store
37280              * @param {Number} index The index of the selected item in the dropdown list
37281              */
37282         'beforeselect' : true,
37283         /**
37284          * @event select
37285          * Fires when a list item is selected
37286              * @param {Roo.form.ComboBox} combo This combo box
37287              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37288              * @param {Number} index The index of the selected item in the dropdown list
37289              */
37290         'select' : true,
37291         /**
37292          * @event beforequery
37293          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37294          * The event object passed has these properties:
37295              * @param {Roo.form.ComboBox} combo This combo box
37296              * @param {String} query The query
37297              * @param {Boolean} forceAll true to force "all" query
37298              * @param {Boolean} cancel true to cancel the query
37299              * @param {Object} e The query event object
37300              */
37301         'beforequery': true,
37302          /**
37303          * @event add
37304          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37305              * @param {Roo.form.ComboBox} combo This combo box
37306              */
37307         'add' : true,
37308         /**
37309          * @event edit
37310          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37311              * @param {Roo.form.ComboBox} combo This combo box
37312              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37313              */
37314         'edit' : true
37315         
37316         
37317     });
37318     if(this.transform){
37319         this.allowDomMove = false;
37320         var s = Roo.getDom(this.transform);
37321         if(!this.hiddenName){
37322             this.hiddenName = s.name;
37323         }
37324         if(!this.store){
37325             this.mode = 'local';
37326             var d = [], opts = s.options;
37327             for(var i = 0, len = opts.length;i < len; i++){
37328                 var o = opts[i];
37329                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37330                 if(o.selected) {
37331                     this.value = value;
37332                 }
37333                 d.push([value, o.text]);
37334             }
37335             this.store = new Roo.data.SimpleStore({
37336                 'id': 0,
37337                 fields: ['value', 'text'],
37338                 data : d
37339             });
37340             this.valueField = 'value';
37341             this.displayField = 'text';
37342         }
37343         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37344         if(!this.lazyRender){
37345             this.target = true;
37346             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37347             s.parentNode.removeChild(s); // remove it
37348             this.render(this.el.parentNode);
37349         }else{
37350             s.parentNode.removeChild(s); // remove it
37351         }
37352
37353     }
37354     if (this.store) {
37355         this.store = Roo.factory(this.store, Roo.data);
37356     }
37357     
37358     this.selectedIndex = -1;
37359     if(this.mode == 'local'){
37360         if(config.queryDelay === undefined){
37361             this.queryDelay = 10;
37362         }
37363         if(config.minChars === undefined){
37364             this.minChars = 0;
37365         }
37366     }
37367 };
37368
37369 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37370     /**
37371      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37372      */
37373     /**
37374      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37375      * rendering into an Roo.Editor, defaults to false)
37376      */
37377     /**
37378      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37379      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37380      */
37381     /**
37382      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37383      */
37384     /**
37385      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37386      * the dropdown list (defaults to undefined, with no header element)
37387      */
37388
37389      /**
37390      * @cfg {String/Roo.Template} tpl The template to use to render the output
37391      */
37392      
37393     // private
37394     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37395     /**
37396      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37397      */
37398     listWidth: undefined,
37399     /**
37400      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37401      * mode = 'remote' or 'text' if mode = 'local')
37402      */
37403     displayField: undefined,
37404     /**
37405      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37406      * mode = 'remote' or 'value' if mode = 'local'). 
37407      * Note: use of a valueField requires the user make a selection
37408      * in order for a value to be mapped.
37409      */
37410     valueField: undefined,
37411     
37412     
37413     /**
37414      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37415      * field's data value (defaults to the underlying DOM element's name)
37416      */
37417     hiddenName: undefined,
37418     /**
37419      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37420      */
37421     listClass: '',
37422     /**
37423      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37424      */
37425     selectedClass: 'x-combo-selected',
37426     /**
37427      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37428      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37429      * which displays a downward arrow icon).
37430      */
37431     triggerClass : 'x-form-arrow-trigger',
37432     /**
37433      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37434      */
37435     shadow:'sides',
37436     /**
37437      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37438      * anchor positions (defaults to 'tl-bl')
37439      */
37440     listAlign: 'tl-bl?',
37441     /**
37442      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37443      */
37444     maxHeight: 300,
37445     /**
37446      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37447      * query specified by the allQuery config option (defaults to 'query')
37448      */
37449     triggerAction: 'query',
37450     /**
37451      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37452      * (defaults to 4, does not apply if editable = false)
37453      */
37454     minChars : 4,
37455     /**
37456      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37457      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37458      */
37459     typeAhead: false,
37460     /**
37461      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37462      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37463      */
37464     queryDelay: 500,
37465     /**
37466      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37467      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37468      */
37469     pageSize: 0,
37470     /**
37471      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37472      * when editable = true (defaults to false)
37473      */
37474     selectOnFocus:false,
37475     /**
37476      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37477      */
37478     queryParam: 'query',
37479     /**
37480      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37481      * when mode = 'remote' (defaults to 'Loading...')
37482      */
37483     loadingText: 'Loading...',
37484     /**
37485      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37486      */
37487     resizable: false,
37488     /**
37489      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37490      */
37491     handleHeight : 8,
37492     /**
37493      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37494      * traditional select (defaults to true)
37495      */
37496     editable: true,
37497     /**
37498      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37499      */
37500     allQuery: '',
37501     /**
37502      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37503      */
37504     mode: 'remote',
37505     /**
37506      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37507      * listWidth has a higher value)
37508      */
37509     minListWidth : 70,
37510     /**
37511      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37512      * allow the user to set arbitrary text into the field (defaults to false)
37513      */
37514     forceSelection:false,
37515     /**
37516      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37517      * if typeAhead = true (defaults to 250)
37518      */
37519     typeAheadDelay : 250,
37520     /**
37521      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37522      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37523      */
37524     valueNotFoundText : undefined,
37525     /**
37526      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37527      */
37528     blockFocus : false,
37529     
37530     /**
37531      * @cfg {Boolean} disableClear Disable showing of clear button.
37532      */
37533     disableClear : false,
37534     /**
37535      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37536      */
37537     alwaysQuery : false,
37538     
37539     //private
37540     addicon : false,
37541     editicon: false,
37542     
37543     // element that contains real text value.. (when hidden is used..)
37544      
37545     // private
37546     onRender : function(ct, position){
37547         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37548         if(this.hiddenName){
37549             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37550                     'before', true);
37551             this.hiddenField.value =
37552                 this.hiddenValue !== undefined ? this.hiddenValue :
37553                 this.value !== undefined ? this.value : '';
37554
37555             // prevent input submission
37556             this.el.dom.removeAttribute('name');
37557              
37558              
37559         }
37560         if(Roo.isGecko){
37561             this.el.dom.setAttribute('autocomplete', 'off');
37562         }
37563
37564         var cls = 'x-combo-list';
37565
37566         this.list = new Roo.Layer({
37567             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37568         });
37569
37570         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37571         this.list.setWidth(lw);
37572         this.list.swallowEvent('mousewheel');
37573         this.assetHeight = 0;
37574
37575         if(this.title){
37576             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37577             this.assetHeight += this.header.getHeight();
37578         }
37579
37580         this.innerList = this.list.createChild({cls:cls+'-inner'});
37581         this.innerList.on('mouseover', this.onViewOver, this);
37582         this.innerList.on('mousemove', this.onViewMove, this);
37583         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37584         
37585         if(this.allowBlank && !this.pageSize && !this.disableClear){
37586             this.footer = this.list.createChild({cls:cls+'-ft'});
37587             this.pageTb = new Roo.Toolbar(this.footer);
37588            
37589         }
37590         if(this.pageSize){
37591             this.footer = this.list.createChild({cls:cls+'-ft'});
37592             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37593                     {pageSize: this.pageSize});
37594             
37595         }
37596         
37597         if (this.pageTb && this.allowBlank && !this.disableClear) {
37598             var _this = this;
37599             this.pageTb.add(new Roo.Toolbar.Fill(), {
37600                 cls: 'x-btn-icon x-btn-clear',
37601                 text: '&#160;',
37602                 handler: function()
37603                 {
37604                     _this.collapse();
37605                     _this.clearValue();
37606                     _this.onSelect(false, -1);
37607                 }
37608             });
37609         }
37610         if (this.footer) {
37611             this.assetHeight += this.footer.getHeight();
37612         }
37613         
37614
37615         if(!this.tpl){
37616             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37617         }
37618
37619         this.view = new Roo.View(this.innerList, this.tpl, {
37620             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37621         });
37622
37623         this.view.on('click', this.onViewClick, this);
37624
37625         this.store.on('beforeload', this.onBeforeLoad, this);
37626         this.store.on('load', this.onLoad, this);
37627         this.store.on('loadexception', this.onLoadException, this);
37628
37629         if(this.resizable){
37630             this.resizer = new Roo.Resizable(this.list,  {
37631                pinned:true, handles:'se'
37632             });
37633             this.resizer.on('resize', function(r, w, h){
37634                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37635                 this.listWidth = w;
37636                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37637                 this.restrictHeight();
37638             }, this);
37639             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37640         }
37641         if(!this.editable){
37642             this.editable = true;
37643             this.setEditable(false);
37644         }  
37645         
37646         
37647         if (typeof(this.events.add.listeners) != 'undefined') {
37648             
37649             this.addicon = this.wrap.createChild(
37650                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37651        
37652             this.addicon.on('click', function(e) {
37653                 this.fireEvent('add', this);
37654             }, this);
37655         }
37656         if (typeof(this.events.edit.listeners) != 'undefined') {
37657             
37658             this.editicon = this.wrap.createChild(
37659                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37660             if (this.addicon) {
37661                 this.editicon.setStyle('margin-left', '40px');
37662             }
37663             this.editicon.on('click', function(e) {
37664                 
37665                 // we fire even  if inothing is selected..
37666                 this.fireEvent('edit', this, this.lastData );
37667                 
37668             }, this);
37669         }
37670         
37671         
37672         
37673     },
37674
37675     // private
37676     initEvents : function(){
37677         Roo.form.ComboBox.superclass.initEvents.call(this);
37678
37679         this.keyNav = new Roo.KeyNav(this.el, {
37680             "up" : function(e){
37681                 this.inKeyMode = true;
37682                 this.selectPrev();
37683             },
37684
37685             "down" : function(e){
37686                 if(!this.isExpanded()){
37687                     this.onTriggerClick();
37688                 }else{
37689                     this.inKeyMode = true;
37690                     this.selectNext();
37691                 }
37692             },
37693
37694             "enter" : function(e){
37695                 this.onViewClick();
37696                 //return true;
37697             },
37698
37699             "esc" : function(e){
37700                 this.collapse();
37701             },
37702
37703             "tab" : function(e){
37704                 this.onViewClick(false);
37705                 this.fireEvent("specialkey", this, e);
37706                 return true;
37707             },
37708
37709             scope : this,
37710
37711             doRelay : function(foo, bar, hname){
37712                 if(hname == 'down' || this.scope.isExpanded()){
37713                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37714                 }
37715                 return true;
37716             },
37717
37718             forceKeyDown: true
37719         });
37720         this.queryDelay = Math.max(this.queryDelay || 10,
37721                 this.mode == 'local' ? 10 : 250);
37722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37723         if(this.typeAhead){
37724             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37725         }
37726         if(this.editable !== false){
37727             this.el.on("keyup", this.onKeyUp, this);
37728         }
37729         if(this.forceSelection){
37730             this.on('blur', this.doForce, this);
37731         }
37732     },
37733
37734     onDestroy : function(){
37735         if(this.view){
37736             this.view.setStore(null);
37737             this.view.el.removeAllListeners();
37738             this.view.el.remove();
37739             this.view.purgeListeners();
37740         }
37741         if(this.list){
37742             this.list.destroy();
37743         }
37744         if(this.store){
37745             this.store.un('beforeload', this.onBeforeLoad, this);
37746             this.store.un('load', this.onLoad, this);
37747             this.store.un('loadexception', this.onLoadException, this);
37748         }
37749         Roo.form.ComboBox.superclass.onDestroy.call(this);
37750     },
37751
37752     // private
37753     fireKey : function(e){
37754         if(e.isNavKeyPress() && !this.list.isVisible()){
37755             this.fireEvent("specialkey", this, e);
37756         }
37757     },
37758
37759     // private
37760     onResize: function(w, h){
37761         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37762         
37763         if(typeof w != 'number'){
37764             // we do not handle it!?!?
37765             return;
37766         }
37767         var tw = this.trigger.getWidth();
37768         tw += this.addicon ? this.addicon.getWidth() : 0;
37769         tw += this.editicon ? this.editicon.getWidth() : 0;
37770         var x = w - tw;
37771         this.el.setWidth( this.adjustWidth('input', x));
37772             
37773         this.trigger.setStyle('left', x+'px');
37774         
37775         if(this.list && this.listWidth === undefined){
37776             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37777             this.list.setWidth(lw);
37778             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37779         }
37780         
37781     
37782         
37783     },
37784
37785     /**
37786      * Allow or prevent the user from directly editing the field text.  If false is passed,
37787      * the user will only be able to select from the items defined in the dropdown list.  This method
37788      * is the runtime equivalent of setting the 'editable' config option at config time.
37789      * @param {Boolean} value True to allow the user to directly edit the field text
37790      */
37791     setEditable : function(value){
37792         if(value == this.editable){
37793             return;
37794         }
37795         this.editable = value;
37796         if(!value){
37797             this.el.dom.setAttribute('readOnly', true);
37798             this.el.on('mousedown', this.onTriggerClick,  this);
37799             this.el.addClass('x-combo-noedit');
37800         }else{
37801             this.el.dom.setAttribute('readOnly', false);
37802             this.el.un('mousedown', this.onTriggerClick,  this);
37803             this.el.removeClass('x-combo-noedit');
37804         }
37805     },
37806
37807     // private
37808     onBeforeLoad : function(){
37809         if(!this.hasFocus){
37810             return;
37811         }
37812         this.innerList.update(this.loadingText ?
37813                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37814         this.restrictHeight();
37815         this.selectedIndex = -1;
37816     },
37817
37818     // private
37819     onLoad : function(){
37820         if(!this.hasFocus){
37821             return;
37822         }
37823         if(this.store.getCount() > 0){
37824             this.expand();
37825             this.restrictHeight();
37826             if(this.lastQuery == this.allQuery){
37827                 if(this.editable){
37828                     this.el.dom.select();
37829                 }
37830                 if(!this.selectByValue(this.value, true)){
37831                     this.select(0, true);
37832                 }
37833             }else{
37834                 this.selectNext();
37835                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37836                     this.taTask.delay(this.typeAheadDelay);
37837                 }
37838             }
37839         }else{
37840             this.onEmptyResults();
37841         }
37842         //this.el.focus();
37843     },
37844     // private
37845     onLoadException : function()
37846     {
37847         this.collapse();
37848         Roo.log(this.store.reader.jsonData);
37849         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37850             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37851         }
37852         
37853         
37854     },
37855     // private
37856     onTypeAhead : function(){
37857         if(this.store.getCount() > 0){
37858             var r = this.store.getAt(0);
37859             var newValue = r.data[this.displayField];
37860             var len = newValue.length;
37861             var selStart = this.getRawValue().length;
37862             if(selStart != len){
37863                 this.setRawValue(newValue);
37864                 this.selectText(selStart, newValue.length);
37865             }
37866         }
37867     },
37868
37869     // private
37870     onSelect : function(record, index){
37871         if(this.fireEvent('beforeselect', this, record, index) !== false){
37872             this.setFromData(index > -1 ? record.data : false);
37873             this.collapse();
37874             this.fireEvent('select', this, record, index);
37875         }
37876     },
37877
37878     /**
37879      * Returns the currently selected field value or empty string if no value is set.
37880      * @return {String} value The selected value
37881      */
37882     getValue : function(){
37883         if(this.valueField){
37884             return typeof this.value != 'undefined' ? this.value : '';
37885         }else{
37886             return Roo.form.ComboBox.superclass.getValue.call(this);
37887         }
37888     },
37889
37890     /**
37891      * Clears any text/value currently set in the field
37892      */
37893     clearValue : function(){
37894         if(this.hiddenField){
37895             this.hiddenField.value = '';
37896         }
37897         this.value = '';
37898         this.setRawValue('');
37899         this.lastSelectionText = '';
37900         this.applyEmptyText();
37901     },
37902
37903     /**
37904      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37905      * will be displayed in the field.  If the value does not match the data value of an existing item,
37906      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37907      * Otherwise the field will be blank (although the value will still be set).
37908      * @param {String} value The value to match
37909      */
37910     setValue : function(v){
37911         var text = v;
37912         if(this.valueField){
37913             var r = this.findRecord(this.valueField, v);
37914             if(r){
37915                 text = r.data[this.displayField];
37916             }else if(this.valueNotFoundText !== undefined){
37917                 text = this.valueNotFoundText;
37918             }
37919         }
37920         this.lastSelectionText = text;
37921         if(this.hiddenField){
37922             this.hiddenField.value = v;
37923         }
37924         Roo.form.ComboBox.superclass.setValue.call(this, text);
37925         this.value = v;
37926     },
37927     /**
37928      * @property {Object} the last set data for the element
37929      */
37930     
37931     lastData : false,
37932     /**
37933      * Sets the value of the field based on a object which is related to the record format for the store.
37934      * @param {Object} value the value to set as. or false on reset?
37935      */
37936     setFromData : function(o){
37937         var dv = ''; // display value
37938         var vv = ''; // value value..
37939         this.lastData = o;
37940         if (this.displayField) {
37941             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37942         } else {
37943             // this is an error condition!!!
37944             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37945         }
37946         
37947         if(this.valueField){
37948             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37949         }
37950         if(this.hiddenField){
37951             this.hiddenField.value = vv;
37952             
37953             this.lastSelectionText = dv;
37954             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37955             this.value = vv;
37956             return;
37957         }
37958         // no hidden field.. - we store the value in 'value', but still display
37959         // display field!!!!
37960         this.lastSelectionText = dv;
37961         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37962         this.value = vv;
37963         
37964         
37965     },
37966     // private
37967     reset : function(){
37968         // overridden so that last data is reset..
37969         this.setValue(this.originalValue);
37970         this.clearInvalid();
37971         this.lastData = false;
37972     },
37973     // private
37974     findRecord : function(prop, value){
37975         var record;
37976         if(this.store.getCount() > 0){
37977             this.store.each(function(r){
37978                 if(r.data[prop] == value){
37979                     record = r;
37980                     return false;
37981                 }
37982                 return true;
37983             });
37984         }
37985         return record;
37986     },
37987     
37988     getName: function()
37989     {
37990         // returns hidden if it's set..
37991         if (!this.rendered) {return ''};
37992         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37993         
37994     },
37995     // private
37996     onViewMove : function(e, t){
37997         this.inKeyMode = false;
37998     },
37999
38000     // private
38001     onViewOver : function(e, t){
38002         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38003             return;
38004         }
38005         var item = this.view.findItemFromChild(t);
38006         if(item){
38007             var index = this.view.indexOf(item);
38008             this.select(index, false);
38009         }
38010     },
38011
38012     // private
38013     onViewClick : function(doFocus)
38014     {
38015         var index = this.view.getSelectedIndexes()[0];
38016         var r = this.store.getAt(index);
38017         if(r){
38018             this.onSelect(r, index);
38019         }
38020         if(doFocus !== false && !this.blockFocus){
38021             this.el.focus();
38022         }
38023     },
38024
38025     // private
38026     restrictHeight : function(){
38027         this.innerList.dom.style.height = '';
38028         var inner = this.innerList.dom;
38029         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38030         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38031         this.list.beginUpdate();
38032         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38033         this.list.alignTo(this.el, this.listAlign);
38034         this.list.endUpdate();
38035     },
38036
38037     // private
38038     onEmptyResults : function(){
38039         this.collapse();
38040     },
38041
38042     /**
38043      * Returns true if the dropdown list is expanded, else false.
38044      */
38045     isExpanded : function(){
38046         return this.list.isVisible();
38047     },
38048
38049     /**
38050      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38051      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38052      * @param {String} value The data value of the item to select
38053      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38054      * selected item if it is not currently in view (defaults to true)
38055      * @return {Boolean} True if the value matched an item in the list, else false
38056      */
38057     selectByValue : function(v, scrollIntoView){
38058         if(v !== undefined && v !== null){
38059             var r = this.findRecord(this.valueField || this.displayField, v);
38060             if(r){
38061                 this.select(this.store.indexOf(r), scrollIntoView);
38062                 return true;
38063             }
38064         }
38065         return false;
38066     },
38067
38068     /**
38069      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38070      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38071      * @param {Number} index The zero-based index of the list item to select
38072      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38073      * selected item if it is not currently in view (defaults to true)
38074      */
38075     select : function(index, scrollIntoView){
38076         this.selectedIndex = index;
38077         this.view.select(index);
38078         if(scrollIntoView !== false){
38079             var el = this.view.getNode(index);
38080             if(el){
38081                 this.innerList.scrollChildIntoView(el, false);
38082             }
38083         }
38084     },
38085
38086     // private
38087     selectNext : function(){
38088         var ct = this.store.getCount();
38089         if(ct > 0){
38090             if(this.selectedIndex == -1){
38091                 this.select(0);
38092             }else if(this.selectedIndex < ct-1){
38093                 this.select(this.selectedIndex+1);
38094             }
38095         }
38096     },
38097
38098     // private
38099     selectPrev : function(){
38100         var ct = this.store.getCount();
38101         if(ct > 0){
38102             if(this.selectedIndex == -1){
38103                 this.select(0);
38104             }else if(this.selectedIndex != 0){
38105                 this.select(this.selectedIndex-1);
38106             }
38107         }
38108     },
38109
38110     // private
38111     onKeyUp : function(e){
38112         if(this.editable !== false && !e.isSpecialKey()){
38113             this.lastKey = e.getKey();
38114             this.dqTask.delay(this.queryDelay);
38115         }
38116     },
38117
38118     // private
38119     validateBlur : function(){
38120         return !this.list || !this.list.isVisible();   
38121     },
38122
38123     // private
38124     initQuery : function(){
38125         this.doQuery(this.getRawValue());
38126     },
38127
38128     // private
38129     doForce : function(){
38130         if(this.el.dom.value.length > 0){
38131             this.el.dom.value =
38132                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38133             this.applyEmptyText();
38134         }
38135     },
38136
38137     /**
38138      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38139      * query allowing the query action to be canceled if needed.
38140      * @param {String} query The SQL query to execute
38141      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38142      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38143      * saved in the current store (defaults to false)
38144      */
38145     doQuery : function(q, forceAll){
38146         if(q === undefined || q === null){
38147             q = '';
38148         }
38149         var qe = {
38150             query: q,
38151             forceAll: forceAll,
38152             combo: this,
38153             cancel:false
38154         };
38155         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38156             return false;
38157         }
38158         q = qe.query;
38159         forceAll = qe.forceAll;
38160         if(forceAll === true || (q.length >= this.minChars)){
38161             if(this.lastQuery != q || this.alwaysQuery){
38162                 this.lastQuery = q;
38163                 if(this.mode == 'local'){
38164                     this.selectedIndex = -1;
38165                     if(forceAll){
38166                         this.store.clearFilter();
38167                     }else{
38168                         this.store.filter(this.displayField, q);
38169                     }
38170                     this.onLoad();
38171                 }else{
38172                     this.store.baseParams[this.queryParam] = q;
38173                     this.store.load({
38174                         params: this.getParams(q)
38175                     });
38176                     this.expand();
38177                 }
38178             }else{
38179                 this.selectedIndex = -1;
38180                 this.onLoad();   
38181             }
38182         }
38183     },
38184
38185     // private
38186     getParams : function(q){
38187         var p = {};
38188         //p[this.queryParam] = q;
38189         if(this.pageSize){
38190             p.start = 0;
38191             p.limit = this.pageSize;
38192         }
38193         return p;
38194     },
38195
38196     /**
38197      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38198      */
38199     collapse : function(){
38200         if(!this.isExpanded()){
38201             return;
38202         }
38203         this.list.hide();
38204         Roo.get(document).un('mousedown', this.collapseIf, this);
38205         Roo.get(document).un('mousewheel', this.collapseIf, this);
38206         if (!this.editable) {
38207             Roo.get(document).un('keydown', this.listKeyPress, this);
38208         }
38209         this.fireEvent('collapse', this);
38210     },
38211
38212     // private
38213     collapseIf : function(e){
38214         if(!e.within(this.wrap) && !e.within(this.list)){
38215             this.collapse();
38216         }
38217     },
38218
38219     /**
38220      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38221      */
38222     expand : function(){
38223         if(this.isExpanded() || !this.hasFocus){
38224             return;
38225         }
38226         this.list.alignTo(this.el, this.listAlign);
38227         this.list.show();
38228         Roo.get(document).on('mousedown', this.collapseIf, this);
38229         Roo.get(document).on('mousewheel', this.collapseIf, this);
38230         if (!this.editable) {
38231             Roo.get(document).on('keydown', this.listKeyPress, this);
38232         }
38233         
38234         this.fireEvent('expand', this);
38235     },
38236
38237     // private
38238     // Implements the default empty TriggerField.onTriggerClick function
38239     onTriggerClick : function(){
38240         if(this.disabled){
38241             return;
38242         }
38243         if(this.isExpanded()){
38244             this.collapse();
38245             if (!this.blockFocus) {
38246                 this.el.focus();
38247             }
38248             
38249         }else {
38250             this.hasFocus = true;
38251             if(this.triggerAction == 'all') {
38252                 this.doQuery(this.allQuery, true);
38253             } else {
38254                 this.doQuery(this.getRawValue());
38255             }
38256             if (!this.blockFocus) {
38257                 this.el.focus();
38258             }
38259         }
38260     },
38261     listKeyPress : function(e)
38262     {
38263         //Roo.log('listkeypress');
38264         // scroll to first matching element based on key pres..
38265         if (e.isSpecialKey()) {
38266             return false;
38267         }
38268         var k = String.fromCharCode(e.getKey()).toUpperCase();
38269         //Roo.log(k);
38270         var match  = false;
38271         var csel = this.view.getSelectedNodes();
38272         var cselitem = false;
38273         if (csel.length) {
38274             var ix = this.view.indexOf(csel[0]);
38275             cselitem  = this.store.getAt(ix);
38276             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38277                 cselitem = false;
38278             }
38279             
38280         }
38281         
38282         this.store.each(function(v) { 
38283             if (cselitem) {
38284                 // start at existing selection.
38285                 if (cselitem.id == v.id) {
38286                     cselitem = false;
38287                 }
38288                 return;
38289             }
38290                 
38291             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38292                 match = this.store.indexOf(v);
38293                 return false;
38294             }
38295         }, this);
38296         
38297         if (match === false) {
38298             return true; // no more action?
38299         }
38300         // scroll to?
38301         this.view.select(match);
38302         var sn = Roo.get(this.view.getSelectedNodes()[0])
38303         sn.scrollIntoView(sn.dom.parentNode, false);
38304     }
38305
38306     /** 
38307     * @cfg {Boolean} grow 
38308     * @hide 
38309     */
38310     /** 
38311     * @cfg {Number} growMin 
38312     * @hide 
38313     */
38314     /** 
38315     * @cfg {Number} growMax 
38316     * @hide 
38317     */
38318     /**
38319      * @hide
38320      * @method autoSize
38321      */
38322 });/*
38323  * Based on:
38324  * Ext JS Library 1.1.1
38325  * Copyright(c) 2006-2007, Ext JS, LLC.
38326  *
38327  * Originally Released Under LGPL - original licence link has changed is not relivant.
38328  *
38329  * Fork - LGPL
38330  * <script type="text/javascript">
38331  */
38332 /**
38333  * @class Roo.form.Checkbox
38334  * @extends Roo.form.Field
38335  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38336  * @constructor
38337  * Creates a new Checkbox
38338  * @param {Object} config Configuration options
38339  */
38340 Roo.form.Checkbox = function(config){
38341     Roo.form.Checkbox.superclass.constructor.call(this, config);
38342     this.addEvents({
38343         /**
38344          * @event check
38345          * Fires when the checkbox is checked or unchecked.
38346              * @param {Roo.form.Checkbox} this This checkbox
38347              * @param {Boolean} checked The new checked value
38348              */
38349         check : true
38350     });
38351 };
38352
38353 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38354     /**
38355      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38356      */
38357     focusClass : undefined,
38358     /**
38359      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38360      */
38361     fieldClass: "x-form-field",
38362     /**
38363      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38364      */
38365     checked: false,
38366     /**
38367      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38368      * {tag: "input", type: "checkbox", autocomplete: "off"})
38369      */
38370     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38371     /**
38372      * @cfg {String} boxLabel The text that appears beside the checkbox
38373      */
38374     boxLabel : "",
38375     /**
38376      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38377      */  
38378     inputValue : '1',
38379     /**
38380      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38381      */
38382      valueOff: '0', // value when not checked..
38383
38384     actionMode : 'viewEl', 
38385     //
38386     // private
38387     itemCls : 'x-menu-check-item x-form-item',
38388     groupClass : 'x-menu-group-item',
38389     inputType : 'hidden',
38390     
38391     
38392     inSetChecked: false, // check that we are not calling self...
38393     
38394     inputElement: false, // real input element?
38395     basedOn: false, // ????
38396     
38397     isFormField: true, // not sure where this is needed!!!!
38398
38399     onResize : function(){
38400         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38401         if(!this.boxLabel){
38402             this.el.alignTo(this.wrap, 'c-c');
38403         }
38404     },
38405
38406     initEvents : function(){
38407         Roo.form.Checkbox.superclass.initEvents.call(this);
38408         this.el.on("click", this.onClick,  this);
38409         this.el.on("change", this.onClick,  this);
38410     },
38411
38412
38413     getResizeEl : function(){
38414         return this.wrap;
38415     },
38416
38417     getPositionEl : function(){
38418         return this.wrap;
38419     },
38420
38421     // private
38422     onRender : function(ct, position){
38423         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38424         /*
38425         if(this.inputValue !== undefined){
38426             this.el.dom.value = this.inputValue;
38427         }
38428         */
38429         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38430         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38431         var viewEl = this.wrap.createChild({ 
38432             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38433         this.viewEl = viewEl;   
38434         this.wrap.on('click', this.onClick,  this); 
38435         
38436         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38437         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38438         
38439         
38440         
38441         if(this.boxLabel){
38442             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38443         //    viewEl.on('click', this.onClick,  this); 
38444         }
38445         //if(this.checked){
38446             this.setChecked(this.checked);
38447         //}else{
38448             //this.checked = this.el.dom;
38449         //}
38450
38451     },
38452
38453     // private
38454     initValue : Roo.emptyFn,
38455
38456     /**
38457      * Returns the checked state of the checkbox.
38458      * @return {Boolean} True if checked, else false
38459      */
38460     getValue : function(){
38461         if(this.el){
38462             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38463         }
38464         return this.valueOff;
38465         
38466     },
38467
38468         // private
38469     onClick : function(){ 
38470         this.setChecked(!this.checked);
38471
38472         //if(this.el.dom.checked != this.checked){
38473         //    this.setValue(this.el.dom.checked);
38474        // }
38475     },
38476
38477     /**
38478      * Sets the checked state of the checkbox.
38479      * On is always based on a string comparison between inputValue and the param.
38480      * @param {Boolean/String} value - the value to set 
38481      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38482      */
38483     setValue : function(v,suppressEvent){
38484         
38485         
38486         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38487         //if(this.el && this.el.dom){
38488         //    this.el.dom.checked = this.checked;
38489         //    this.el.dom.defaultChecked = this.checked;
38490         //}
38491         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38492         //this.fireEvent("check", this, this.checked);
38493     },
38494     // private..
38495     setChecked : function(state,suppressEvent)
38496     {
38497         if (this.inSetChecked) {
38498             this.checked = state;
38499             return;
38500         }
38501         
38502     
38503         if(this.wrap){
38504             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38505         }
38506         this.checked = state;
38507         if(suppressEvent !== true){
38508             this.fireEvent('check', this, state);
38509         }
38510         this.inSetChecked = true;
38511         this.el.dom.value = state ? this.inputValue : this.valueOff;
38512         this.inSetChecked = false;
38513         
38514     },
38515     // handle setting of hidden value by some other method!!?!?
38516     setFromHidden: function()
38517     {
38518         if(!this.el){
38519             return;
38520         }
38521         //console.log("SET FROM HIDDEN");
38522         //alert('setFrom hidden');
38523         this.setValue(this.el.dom.value);
38524     },
38525     
38526     onDestroy : function()
38527     {
38528         if(this.viewEl){
38529             Roo.get(this.viewEl).remove();
38530         }
38531          
38532         Roo.form.Checkbox.superclass.onDestroy.call(this);
38533     }
38534
38535 });/*
38536  * Based on:
38537  * Ext JS Library 1.1.1
38538  * Copyright(c) 2006-2007, Ext JS, LLC.
38539  *
38540  * Originally Released Under LGPL - original licence link has changed is not relivant.
38541  *
38542  * Fork - LGPL
38543  * <script type="text/javascript">
38544  */
38545  
38546 /**
38547  * @class Roo.form.Radio
38548  * @extends Roo.form.Checkbox
38549  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38550  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38551  * @constructor
38552  * Creates a new Radio
38553  * @param {Object} config Configuration options
38554  */
38555 Roo.form.Radio = function(){
38556     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38557 };
38558 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38559     inputType: 'radio',
38560
38561     /**
38562      * If this radio is part of a group, it will return the selected value
38563      * @return {String}
38564      */
38565     getGroupValue : function(){
38566         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38567     }
38568 });//<script type="text/javascript">
38569
38570 /*
38571  * Ext JS Library 1.1.1
38572  * Copyright(c) 2006-2007, Ext JS, LLC.
38573  * licensing@extjs.com
38574  * 
38575  * http://www.extjs.com/license
38576  */
38577  
38578  /*
38579   * 
38580   * Known bugs:
38581   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38582   * - IE ? - no idea how much works there.
38583   * 
38584   * 
38585   * 
38586   */
38587  
38588
38589 /**
38590  * @class Ext.form.HtmlEditor
38591  * @extends Ext.form.Field
38592  * Provides a lightweight HTML Editor component.
38593  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38594  * 
38595  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38596  * supported by this editor.</b><br/><br/>
38597  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38598  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38599  */
38600 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38601       /**
38602      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38603      */
38604     toolbars : false,
38605     /**
38606      * @cfg {String} createLinkText The default text for the create link prompt
38607      */
38608     createLinkText : 'Please enter the URL for the link:',
38609     /**
38610      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38611      */
38612     defaultLinkValue : 'http:/'+'/',
38613    
38614      /**
38615      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38616      *                        Roo.resizable.
38617      */
38618     resizable : false,
38619      /**
38620      * @cfg {Number} height (in pixels)
38621      */   
38622     height: 300,
38623    /**
38624      * @cfg {Number} width (in pixels)
38625      */   
38626     width: 500,
38627     
38628     /**
38629      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38630      * 
38631      */
38632     stylesheets: false,
38633     
38634     // id of frame..
38635     frameId: false,
38636     
38637     // private properties
38638     validationEvent : false,
38639     deferHeight: true,
38640     initialized : false,
38641     activated : false,
38642     sourceEditMode : false,
38643     onFocus : Roo.emptyFn,
38644     iframePad:3,
38645     hideMode:'offsets',
38646     
38647     defaultAutoCreate : { // modified by initCompnoent..
38648         tag: "textarea",
38649         style:"width:500px;height:300px;",
38650         autocomplete: "off"
38651     },
38652
38653     // private
38654     initComponent : function(){
38655         this.addEvents({
38656             /**
38657              * @event initialize
38658              * Fires when the editor is fully initialized (including the iframe)
38659              * @param {HtmlEditor} this
38660              */
38661             initialize: true,
38662             /**
38663              * @event activate
38664              * Fires when the editor is first receives the focus. Any insertion must wait
38665              * until after this event.
38666              * @param {HtmlEditor} this
38667              */
38668             activate: true,
38669              /**
38670              * @event beforesync
38671              * Fires before the textarea is updated with content from the editor iframe. Return false
38672              * to cancel the sync.
38673              * @param {HtmlEditor} this
38674              * @param {String} html
38675              */
38676             beforesync: true,
38677              /**
38678              * @event beforepush
38679              * Fires before the iframe editor is updated with content from the textarea. Return false
38680              * to cancel the push.
38681              * @param {HtmlEditor} this
38682              * @param {String} html
38683              */
38684             beforepush: true,
38685              /**
38686              * @event sync
38687              * Fires when the textarea is updated with content from the editor iframe.
38688              * @param {HtmlEditor} this
38689              * @param {String} html
38690              */
38691             sync: true,
38692              /**
38693              * @event push
38694              * Fires when the iframe editor is updated with content from the textarea.
38695              * @param {HtmlEditor} this
38696              * @param {String} html
38697              */
38698             push: true,
38699              /**
38700              * @event editmodechange
38701              * Fires when the editor switches edit modes
38702              * @param {HtmlEditor} this
38703              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38704              */
38705             editmodechange: true,
38706             /**
38707              * @event editorevent
38708              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38709              * @param {HtmlEditor} this
38710              */
38711             editorevent: true
38712         });
38713         this.defaultAutoCreate =  {
38714             tag: "textarea",
38715             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38716             autocomplete: "off"
38717         };
38718     },
38719
38720     /**
38721      * Protected method that will not generally be called directly. It
38722      * is called when the editor creates its toolbar. Override this method if you need to
38723      * add custom toolbar buttons.
38724      * @param {HtmlEditor} editor
38725      */
38726     createToolbar : function(editor){
38727         if (!editor.toolbars || !editor.toolbars.length) {
38728             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38729         }
38730         
38731         for (var i =0 ; i < editor.toolbars.length;i++) {
38732             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38733             editor.toolbars[i].init(editor);
38734         }
38735          
38736         
38737     },
38738
38739     /**
38740      * Protected method that will not generally be called directly. It
38741      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38742      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38743      */
38744     getDocMarkup : function(){
38745         // body styles..
38746         var st = '';
38747         if (this.stylesheets === false) {
38748             
38749             Roo.get(document.head).select('style').each(function(node) {
38750                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38751             });
38752             
38753             Roo.get(document.head).select('link').each(function(node) { 
38754                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38755             });
38756             
38757         } else if (!this.stylesheets.length) {
38758                 // simple..
38759                 st = '<style type="text/css">' +
38760                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38761                    '</style>';
38762         } else {
38763             Roo.each(this.stylesheets, function(s) {
38764                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38765             });
38766             
38767         }
38768         
38769         return '<html><head>' + st  +
38770             //<style type="text/css">' +
38771             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38772             //'</style>' +
38773             ' </head><body></body></html>';
38774     },
38775
38776     // private
38777     onRender : function(ct, position)
38778     {
38779         var _t = this;
38780         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38781         this.el.dom.style.border = '0 none';
38782         this.el.dom.setAttribute('tabIndex', -1);
38783         this.el.addClass('x-hidden');
38784         if(Roo.isIE){ // fix IE 1px bogus margin
38785             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38786         }
38787         this.wrap = this.el.wrap({
38788             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38789         });
38790         
38791         if (this.resizable) {
38792             this.resizeEl = new Roo.Resizable(this.wrap, {
38793                 pinned : true,
38794                 wrap: true,
38795                 dynamic : true,
38796                 minHeight : this.height,
38797                 height: this.height,
38798                 handles : this.resizable,
38799                 width: this.width,
38800                 listeners : {
38801                     resize : function(r, w, h) {
38802                         _t.onResize(w,h); // -something
38803                     }
38804                 }
38805             });
38806             
38807         }
38808
38809         this.frameId = Roo.id();
38810         
38811         this.createToolbar(this);
38812         
38813       
38814         
38815         var iframe = this.wrap.createChild({
38816             tag: 'iframe',
38817             id: this.frameId,
38818             name: this.frameId,
38819             frameBorder : 'no',
38820             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38821         }, this.el
38822         );
38823         
38824        // console.log(iframe);
38825         //this.wrap.dom.appendChild(iframe);
38826
38827         this.iframe = iframe.dom;
38828
38829          this.assignDocWin();
38830         
38831         this.doc.designMode = 'on';
38832        
38833         this.doc.open();
38834         this.doc.write(this.getDocMarkup());
38835         this.doc.close();
38836
38837         
38838         var task = { // must defer to wait for browser to be ready
38839             run : function(){
38840                 //console.log("run task?" + this.doc.readyState);
38841                 this.assignDocWin();
38842                 if(this.doc.body || this.doc.readyState == 'complete'){
38843                     try {
38844                         this.doc.designMode="on";
38845                     } catch (e) {
38846                         return;
38847                     }
38848                     Roo.TaskMgr.stop(task);
38849                     this.initEditor.defer(10, this);
38850                 }
38851             },
38852             interval : 10,
38853             duration:10000,
38854             scope: this
38855         };
38856         Roo.TaskMgr.start(task);
38857
38858         if(!this.width){
38859             this.setSize(this.wrap.getSize());
38860         }
38861         if (this.resizeEl) {
38862             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38863             // should trigger onReize..
38864         }
38865     },
38866
38867     // private
38868     onResize : function(w, h)
38869     {
38870         //Roo.log('resize: ' +w + ',' + h );
38871         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38872         if(this.el && this.iframe){
38873             if(typeof w == 'number'){
38874                 var aw = w - this.wrap.getFrameWidth('lr');
38875                 this.el.setWidth(this.adjustWidth('textarea', aw));
38876                 this.iframe.style.width = aw + 'px';
38877             }
38878             if(typeof h == 'number'){
38879                 var tbh = 0;
38880                 for (var i =0; i < this.toolbars.length;i++) {
38881                     // fixme - ask toolbars for heights?
38882                     tbh += this.toolbars[i].tb.el.getHeight();
38883                     if (this.toolbars[i].footer) {
38884                         tbh += this.toolbars[i].footer.el.getHeight();
38885                     }
38886                 }
38887                 
38888                 
38889                 
38890                 
38891                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38892                 ah -= 5; // knock a few pixes off for look..
38893                 this.el.setHeight(this.adjustWidth('textarea', ah));
38894                 this.iframe.style.height = ah + 'px';
38895                 if(this.doc){
38896                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38897                 }
38898             }
38899         }
38900     },
38901
38902     /**
38903      * Toggles the editor between standard and source edit mode.
38904      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38905      */
38906     toggleSourceEdit : function(sourceEditMode){
38907         
38908         this.sourceEditMode = sourceEditMode === true;
38909         
38910         if(this.sourceEditMode){
38911           
38912             this.syncValue();
38913             this.iframe.className = 'x-hidden';
38914             this.el.removeClass('x-hidden');
38915             this.el.dom.removeAttribute('tabIndex');
38916             this.el.focus();
38917         }else{
38918              
38919             this.pushValue();
38920             this.iframe.className = '';
38921             this.el.addClass('x-hidden');
38922             this.el.dom.setAttribute('tabIndex', -1);
38923             this.deferFocus();
38924         }
38925         this.setSize(this.wrap.getSize());
38926         this.fireEvent('editmodechange', this, this.sourceEditMode);
38927     },
38928
38929     // private used internally
38930     createLink : function(){
38931         var url = prompt(this.createLinkText, this.defaultLinkValue);
38932         if(url && url != 'http:/'+'/'){
38933             this.relayCmd('createlink', url);
38934         }
38935     },
38936
38937     // private (for BoxComponent)
38938     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38939
38940     // private (for BoxComponent)
38941     getResizeEl : function(){
38942         return this.wrap;
38943     },
38944
38945     // private (for BoxComponent)
38946     getPositionEl : function(){
38947         return this.wrap;
38948     },
38949
38950     // private
38951     initEvents : function(){
38952         this.originalValue = this.getValue();
38953     },
38954
38955     /**
38956      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38957      * @method
38958      */
38959     markInvalid : Roo.emptyFn,
38960     /**
38961      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38962      * @method
38963      */
38964     clearInvalid : Roo.emptyFn,
38965
38966     setValue : function(v){
38967         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38968         this.pushValue();
38969     },
38970
38971     /**
38972      * Protected method that will not generally be called directly. If you need/want
38973      * custom HTML cleanup, this is the method you should override.
38974      * @param {String} html The HTML to be cleaned
38975      * return {String} The cleaned HTML
38976      */
38977     cleanHtml : function(html){
38978         html = String(html);
38979         if(html.length > 5){
38980             if(Roo.isSafari){ // strip safari nonsense
38981                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38982             }
38983         }
38984         if(html == '&nbsp;'){
38985             html = '';
38986         }
38987         return html;
38988     },
38989
38990     /**
38991      * Protected method that will not generally be called directly. Syncs the contents
38992      * of the editor iframe with the textarea.
38993      */
38994     syncValue : function(){
38995         if(this.initialized){
38996             var bd = (this.doc.body || this.doc.documentElement);
38997             //this.cleanUpPaste();
38998             var html = bd.innerHTML;
38999             if(Roo.isSafari){
39000                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39001                 var m = bs.match(/text-align:(.*?);/i);
39002                 if(m && m[1]){
39003                     html = '<div style="'+m[0]+'">' + html + '</div>';
39004                 }
39005             }
39006             html = this.cleanHtml(html);
39007             if(this.fireEvent('beforesync', this, html) !== false){
39008                 this.el.dom.value = html;
39009                 this.fireEvent('sync', this, html);
39010             }
39011         }
39012     },
39013
39014     /**
39015      * Protected method that will not generally be called directly. Pushes the value of the textarea
39016      * into the iframe editor.
39017      */
39018     pushValue : function(){
39019         if(this.initialized){
39020             var v = this.el.dom.value;
39021             if(v.length < 1){
39022                 v = '&#160;';
39023             }
39024             
39025             if(this.fireEvent('beforepush', this, v) !== false){
39026                 var d = (this.doc.body || this.doc.documentElement);
39027                 d.innerHTML = v;
39028                 this.cleanUpPaste();
39029                 this.el.dom.value = d.innerHTML;
39030                 this.fireEvent('push', this, v);
39031             }
39032         }
39033     },
39034
39035     // private
39036     deferFocus : function(){
39037         this.focus.defer(10, this);
39038     },
39039
39040     // doc'ed in Field
39041     focus : function(){
39042         if(this.win && !this.sourceEditMode){
39043             this.win.focus();
39044         }else{
39045             this.el.focus();
39046         }
39047     },
39048     
39049     assignDocWin: function()
39050     {
39051         var iframe = this.iframe;
39052         
39053          if(Roo.isIE){
39054             this.doc = iframe.contentWindow.document;
39055             this.win = iframe.contentWindow;
39056         } else {
39057             if (!Roo.get(this.frameId)) {
39058                 return;
39059             }
39060             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39061             this.win = Roo.get(this.frameId).dom.contentWindow;
39062         }
39063     },
39064     
39065     // private
39066     initEditor : function(){
39067         //console.log("INIT EDITOR");
39068         this.assignDocWin();
39069         
39070         
39071         
39072         this.doc.designMode="on";
39073         this.doc.open();
39074         this.doc.write(this.getDocMarkup());
39075         this.doc.close();
39076         
39077         var dbody = (this.doc.body || this.doc.documentElement);
39078         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39079         // this copies styles from the containing element into thsi one..
39080         // not sure why we need all of this..
39081         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39082         ss['background-attachment'] = 'fixed'; // w3c
39083         dbody.bgProperties = 'fixed'; // ie
39084         Roo.DomHelper.applyStyles(dbody, ss);
39085         Roo.EventManager.on(this.doc, {
39086             //'mousedown': this.onEditorEvent,
39087             'mouseup': this.onEditorEvent,
39088             'dblclick': this.onEditorEvent,
39089             'click': this.onEditorEvent,
39090             'keyup': this.onEditorEvent,
39091             buffer:100,
39092             scope: this
39093         });
39094         if(Roo.isGecko){
39095             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39096         }
39097         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39098             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39099         }
39100         this.initialized = true;
39101
39102         this.fireEvent('initialize', this);
39103         this.pushValue();
39104     },
39105
39106     // private
39107     onDestroy : function(){
39108         
39109         
39110         
39111         if(this.rendered){
39112             
39113             for (var i =0; i < this.toolbars.length;i++) {
39114                 // fixme - ask toolbars for heights?
39115                 this.toolbars[i].onDestroy();
39116             }
39117             
39118             this.wrap.dom.innerHTML = '';
39119             this.wrap.remove();
39120         }
39121     },
39122
39123     // private
39124     onFirstFocus : function(){
39125         
39126         this.assignDocWin();
39127         
39128         
39129         this.activated = true;
39130         for (var i =0; i < this.toolbars.length;i++) {
39131             this.toolbars[i].onFirstFocus();
39132         }
39133        
39134         if(Roo.isGecko){ // prevent silly gecko errors
39135             this.win.focus();
39136             var s = this.win.getSelection();
39137             if(!s.focusNode || s.focusNode.nodeType != 3){
39138                 var r = s.getRangeAt(0);
39139                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39140                 r.collapse(true);
39141                 this.deferFocus();
39142             }
39143             try{
39144                 this.execCmd('useCSS', true);
39145                 this.execCmd('styleWithCSS', false);
39146             }catch(e){}
39147         }
39148         this.fireEvent('activate', this);
39149     },
39150
39151     // private
39152     adjustFont: function(btn){
39153         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39154         //if(Roo.isSafari){ // safari
39155         //    adjust *= 2;
39156        // }
39157         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39158         if(Roo.isSafari){ // safari
39159             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39160             v =  (v < 10) ? 10 : v;
39161             v =  (v > 48) ? 48 : v;
39162             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39163             
39164         }
39165         
39166         
39167         v = Math.max(1, v+adjust);
39168         
39169         this.execCmd('FontSize', v  );
39170     },
39171
39172     onEditorEvent : function(e){
39173         this.fireEvent('editorevent', this, e);
39174       //  this.updateToolbar();
39175         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39176     },
39177
39178     insertTag : function(tg)
39179     {
39180         // could be a bit smarter... -> wrap the current selected tRoo..
39181         
39182         this.execCmd("formatblock",   tg);
39183         
39184     },
39185     
39186     insertText : function(txt)
39187     {
39188         
39189         
39190         range = this.createRange();
39191         range.deleteContents();
39192                //alert(Sender.getAttribute('label'));
39193                
39194         range.insertNode(this.doc.createTextNode(txt));
39195     } ,
39196     
39197     // private
39198     relayBtnCmd : function(btn){
39199         this.relayCmd(btn.cmd);
39200     },
39201
39202     /**
39203      * Executes a Midas editor command on the editor document and performs necessary focus and
39204      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39205      * @param {String} cmd The Midas command
39206      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39207      */
39208     relayCmd : function(cmd, value){
39209         this.win.focus();
39210         this.execCmd(cmd, value);
39211         this.fireEvent('editorevent', this);
39212         //this.updateToolbar();
39213         this.deferFocus();
39214     },
39215
39216     /**
39217      * Executes a Midas editor command directly on the editor document.
39218      * For visual commands, you should use {@link #relayCmd} instead.
39219      * <b>This should only be called after the editor is initialized.</b>
39220      * @param {String} cmd The Midas command
39221      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39222      */
39223     execCmd : function(cmd, value){
39224         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39225         this.syncValue();
39226     },
39227
39228    
39229     /**
39230      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39231      * to insert tRoo.
39232      * @param {String} text
39233      */
39234     insertAtCursor : function(text){
39235         if(!this.activated){
39236             return;
39237         }
39238         if(Roo.isIE){
39239             this.win.focus();
39240             var r = this.doc.selection.createRange();
39241             if(r){
39242                 r.collapse(true);
39243                 r.pasteHTML(text);
39244                 this.syncValue();
39245                 this.deferFocus();
39246             }
39247         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39248             this.win.focus();
39249             this.execCmd('InsertHTML', text);
39250             this.deferFocus();
39251         }
39252     },
39253  // private
39254     mozKeyPress : function(e){
39255         if(e.ctrlKey){
39256             var c = e.getCharCode(), cmd;
39257           
39258             if(c > 0){
39259                 c = String.fromCharCode(c).toLowerCase();
39260                 switch(c){
39261                     case 'b':
39262                         cmd = 'bold';
39263                     break;
39264                     case 'i':
39265                         cmd = 'italic';
39266                     break;
39267                     case 'u':
39268                         cmd = 'underline';
39269                         break;
39270                     case 'v':
39271                         this.cleanUpPaste.defer(100, this);
39272                         return;
39273                     break;
39274                 }
39275                 if(cmd){
39276                     this.win.focus();
39277                     this.execCmd(cmd);
39278                     this.deferFocus();
39279                     e.preventDefault();
39280                 }
39281                 
39282             }
39283         }
39284     },
39285
39286     // private
39287     fixKeys : function(){ // load time branching for fastest keydown performance
39288         if(Roo.isIE){
39289             return function(e){
39290                 var k = e.getKey(), r;
39291                 if(k == e.TAB){
39292                     e.stopEvent();
39293                     r = this.doc.selection.createRange();
39294                     if(r){
39295                         r.collapse(true);
39296                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39297                         this.deferFocus();
39298                     }
39299                     return;
39300                 }
39301                 
39302                 if(k == e.ENTER){
39303                     r = this.doc.selection.createRange();
39304                     if(r){
39305                         var target = r.parentElement();
39306                         if(!target || target.tagName.toLowerCase() != 'li'){
39307                             e.stopEvent();
39308                             r.pasteHTML('<br />');
39309                             r.collapse(false);
39310                             r.select();
39311                         }
39312                     }
39313                 }
39314                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39315                     this.cleanUpPaste.defer(100, this);
39316                     return;
39317                 }
39318                 
39319                 
39320             };
39321         }else if(Roo.isOpera){
39322             return function(e){
39323                 var k = e.getKey();
39324                 if(k == e.TAB){
39325                     e.stopEvent();
39326                     this.win.focus();
39327                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39328                     this.deferFocus();
39329                 }
39330                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39331                     this.cleanUpPaste.defer(100, this);
39332                     return;
39333                 }
39334                 
39335             };
39336         }else if(Roo.isSafari){
39337             return function(e){
39338                 var k = e.getKey();
39339                 
39340                 if(k == e.TAB){
39341                     e.stopEvent();
39342                     this.execCmd('InsertText','\t');
39343                     this.deferFocus();
39344                     return;
39345                 }
39346                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39347                     this.cleanUpPaste.defer(100, this);
39348                     return;
39349                 }
39350                 
39351              };
39352         }
39353     }(),
39354     
39355     getAllAncestors: function()
39356     {
39357         var p = this.getSelectedNode();
39358         var a = [];
39359         if (!p) {
39360             a.push(p); // push blank onto stack..
39361             p = this.getParentElement();
39362         }
39363         
39364         
39365         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39366             a.push(p);
39367             p = p.parentNode;
39368         }
39369         a.push(this.doc.body);
39370         return a;
39371     },
39372     lastSel : false,
39373     lastSelNode : false,
39374     
39375     
39376     getSelection : function() 
39377     {
39378         this.assignDocWin();
39379         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39380     },
39381     
39382     getSelectedNode: function() 
39383     {
39384         // this may only work on Gecko!!!
39385         
39386         // should we cache this!!!!
39387         
39388         
39389         
39390          
39391         var range = this.createRange(this.getSelection()).cloneRange();
39392         
39393         if (Roo.isIE) {
39394             var parent = range.parentElement();
39395             while (true) {
39396                 var testRange = range.duplicate();
39397                 testRange.moveToElementText(parent);
39398                 if (testRange.inRange(range)) {
39399                     break;
39400                 }
39401                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39402                     break;
39403                 }
39404                 parent = parent.parentElement;
39405             }
39406             return parent;
39407         }
39408         
39409         // is ancestor a text element.
39410         var ac =  range.commonAncestorContainer;
39411         if (ac.nodeType == 3) {
39412             ac = ac.parentNode;
39413         }
39414         
39415         var ar = ac.childNodes;
39416          
39417         var nodes = [];
39418         var other_nodes = [];
39419         var has_other_nodes = false;
39420         for (var i=0;i<ar.length;i++) {
39421             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39422                 continue;
39423             }
39424             // fullly contained node.
39425             
39426             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39427                 nodes.push(ar[i]);
39428                 continue;
39429             }
39430             
39431             // probably selected..
39432             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39433                 other_nodes.push(ar[i]);
39434                 continue;
39435             }
39436             // outer..
39437             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39438                 continue;
39439             }
39440             
39441             
39442             has_other_nodes = true;
39443         }
39444         if (!nodes.length && other_nodes.length) {
39445             nodes= other_nodes;
39446         }
39447         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39448             return false;
39449         }
39450         
39451         return nodes[0];
39452     },
39453     createRange: function(sel)
39454     {
39455         // this has strange effects when using with 
39456         // top toolbar - not sure if it's a great idea.
39457         //this.editor.contentWindow.focus();
39458         if (typeof sel != "undefined") {
39459             try {
39460                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39461             } catch(e) {
39462                 return this.doc.createRange();
39463             }
39464         } else {
39465             return this.doc.createRange();
39466         }
39467     },
39468     getParentElement: function()
39469     {
39470         
39471         this.assignDocWin();
39472         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39473         
39474         var range = this.createRange(sel);
39475          
39476         try {
39477             var p = range.commonAncestorContainer;
39478             while (p.nodeType == 3) { // text node
39479                 p = p.parentNode;
39480             }
39481             return p;
39482         } catch (e) {
39483             return null;
39484         }
39485     
39486     },
39487     /***
39488      *
39489      * Range intersection.. the hard stuff...
39490      *  '-1' = before
39491      *  '0' = hits..
39492      *  '1' = after.
39493      *         [ -- selected range --- ]
39494      *   [fail]                        [fail]
39495      *
39496      *    basically..
39497      *      if end is before start or  hits it. fail.
39498      *      if start is after end or hits it fail.
39499      *
39500      *   if either hits (but other is outside. - then it's not 
39501      *   
39502      *    
39503      **/
39504     
39505     
39506     // @see http://www.thismuchiknow.co.uk/?p=64.
39507     rangeIntersectsNode : function(range, node)
39508     {
39509         var nodeRange = node.ownerDocument.createRange();
39510         try {
39511             nodeRange.selectNode(node);
39512         } catch (e) {
39513             nodeRange.selectNodeContents(node);
39514         }
39515     
39516         var rangeStartRange = range.cloneRange();
39517         rangeStartRange.collapse(true);
39518     
39519         var rangeEndRange = range.cloneRange();
39520         rangeEndRange.collapse(false);
39521     
39522         var nodeStartRange = nodeRange.cloneRange();
39523         nodeStartRange.collapse(true);
39524     
39525         var nodeEndRange = nodeRange.cloneRange();
39526         nodeEndRange.collapse(false);
39527     
39528         return rangeStartRange.compareBoundaryPoints(
39529                  Range.START_TO_START, nodeEndRange) == -1 &&
39530                rangeEndRange.compareBoundaryPoints(
39531                  Range.START_TO_START, nodeStartRange) == 1;
39532         
39533          
39534     },
39535     rangeCompareNode : function(range, node)
39536     {
39537         var nodeRange = node.ownerDocument.createRange();
39538         try {
39539             nodeRange.selectNode(node);
39540         } catch (e) {
39541             nodeRange.selectNodeContents(node);
39542         }
39543         
39544         
39545         range.collapse(true);
39546     
39547         nodeRange.collapse(true);
39548      
39549         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39550         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39551          
39552         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39553         
39554         var nodeIsBefore   =  ss == 1;
39555         var nodeIsAfter    = ee == -1;
39556         
39557         if (nodeIsBefore && nodeIsAfter)
39558             return 0; // outer
39559         if (!nodeIsBefore && nodeIsAfter)
39560             return 1; //right trailed.
39561         
39562         if (nodeIsBefore && !nodeIsAfter)
39563             return 2;  // left trailed.
39564         // fully contined.
39565         return 3;
39566     },
39567
39568     // private? - in a new class?
39569     cleanUpPaste :  function()
39570     {
39571         // cleans up the whole document..
39572          Roo.log('cleanuppaste');
39573         this.cleanUpChildren(this.doc.body);
39574         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39575         if (clean != this.doc.body.innerHTML) {
39576             this.doc.body.innerHTML = clean;
39577         }
39578         
39579     },
39580     
39581     cleanWordChars : function(input) {
39582         var he = Roo.form.HtmlEditor;
39583     
39584         var output = input;
39585         Roo.each(he.swapCodes, function(sw) { 
39586         
39587             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39588             output = output.replace(swapper, sw[1]);
39589         });
39590         return output;
39591     },
39592     
39593     
39594     cleanUpChildren : function (n)
39595     {
39596         if (!n.childNodes.length) {
39597             return;
39598         }
39599         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39600            this.cleanUpChild(n.childNodes[i]);
39601         }
39602     },
39603     
39604     
39605         
39606     
39607     cleanUpChild : function (node)
39608     {
39609         //console.log(node);
39610         if (node.nodeName == "#text") {
39611             // clean up silly Windows -- stuff?
39612             return; 
39613         }
39614         if (node.nodeName == "#comment") {
39615             node.parentNode.removeChild(node);
39616             // clean up silly Windows -- stuff?
39617             return; 
39618         }
39619         
39620         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39621             // remove node.
39622             node.parentNode.removeChild(node);
39623             return;
39624             
39625         }
39626         
39627         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39628         
39629         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39630         
39631         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39632             remove_keep_children = true;
39633         }
39634         
39635         if (remove_keep_children) {
39636             this.cleanUpChildren(node);
39637             // inserts everything just before this node...
39638             while (node.childNodes.length) {
39639                 var cn = node.childNodes[0];
39640                 node.removeChild(cn);
39641                 node.parentNode.insertBefore(cn, node);
39642             }
39643             node.parentNode.removeChild(node);
39644             return;
39645         }
39646         
39647         if (!node.attributes || !node.attributes.length) {
39648             this.cleanUpChildren(node);
39649             return;
39650         }
39651         
39652         function cleanAttr(n,v)
39653         {
39654             
39655             if (v.match(/^\./) || v.match(/^\//)) {
39656                 return;
39657             }
39658             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39659                 return;
39660             }
39661             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39662             node.removeAttribute(n);
39663             
39664         }
39665         
39666         function cleanStyle(n,v)
39667         {
39668             if (v.match(/expression/)) { //XSS?? should we even bother..
39669                 node.removeAttribute(n);
39670                 return;
39671             }
39672             
39673             
39674             var parts = v.split(/;/);
39675             Roo.each(parts, function(p) {
39676                 p = p.replace(/\s+/g,'');
39677                 if (!p.length) {
39678                     return true;
39679                 }
39680                 var l = p.split(':').shift().replace(/\s+/g,'');
39681                 
39682                 // only allow 'c whitelisted system attributes'
39683                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39684                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39685                     node.removeAttribute(n);
39686                     return false;
39687                 }
39688                 return true;
39689             });
39690             
39691             
39692         }
39693         
39694         
39695         for (var i = node.attributes.length-1; i > -1 ; i--) {
39696             var a = node.attributes[i];
39697             //console.log(a);
39698             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39699                 node.removeAttribute(a.name);
39700                 return;
39701             }
39702             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39703                 cleanAttr(a.name,a.value); // fixme..
39704                 return;
39705             }
39706             if (a.name == 'style') {
39707                 cleanStyle(a.name,a.value);
39708             }
39709             /// clean up MS crap..
39710             // tecnically this should be a list of valid class'es..
39711             
39712             
39713             if (a.name == 'class') {
39714                 if (a.value.match(/^Mso/)) {
39715                     node.className = '';
39716                 }
39717                 
39718                 if (a.value.match(/body/)) {
39719                     node.className = '';
39720                 }
39721             }
39722             
39723             // style cleanup!?
39724             // class cleanup?
39725             
39726         }
39727         
39728         
39729         this.cleanUpChildren(node);
39730         
39731         
39732     }
39733     
39734     
39735     // hide stuff that is not compatible
39736     /**
39737      * @event blur
39738      * @hide
39739      */
39740     /**
39741      * @event change
39742      * @hide
39743      */
39744     /**
39745      * @event focus
39746      * @hide
39747      */
39748     /**
39749      * @event specialkey
39750      * @hide
39751      */
39752     /**
39753      * @cfg {String} fieldClass @hide
39754      */
39755     /**
39756      * @cfg {String} focusClass @hide
39757      */
39758     /**
39759      * @cfg {String} autoCreate @hide
39760      */
39761     /**
39762      * @cfg {String} inputType @hide
39763      */
39764     /**
39765      * @cfg {String} invalidClass @hide
39766      */
39767     /**
39768      * @cfg {String} invalidText @hide
39769      */
39770     /**
39771      * @cfg {String} msgFx @hide
39772      */
39773     /**
39774      * @cfg {String} validateOnBlur @hide
39775      */
39776 });
39777
39778 Roo.form.HtmlEditor.white = [
39779         'area', 'br', 'img', 'input', 'hr', 'wbr',
39780         
39781        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39782        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39783        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39784        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39785        'table',   'ul',         'xmp', 
39786        
39787        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39788       'thead',   'tr', 
39789      
39790       'dir', 'menu', 'ol', 'ul', 'dl',
39791        
39792       'embed',  'object'
39793 ];
39794
39795
39796 Roo.form.HtmlEditor.black = [
39797     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39798         'applet', // 
39799         'base',   'basefont', 'bgsound', 'blink',  'body', 
39800         'frame',  'frameset', 'head',    'html',   'ilayer', 
39801         'iframe', 'layer',  'link',     'meta',    'object',   
39802         'script', 'style' ,'title',  'xml' // clean later..
39803 ];
39804 Roo.form.HtmlEditor.clean = [
39805     'script', 'style', 'title', 'xml'
39806 ];
39807 Roo.form.HtmlEditor.remove = [
39808     'font'
39809 ];
39810 // attributes..
39811
39812 Roo.form.HtmlEditor.ablack = [
39813     'on'
39814 ];
39815     
39816 Roo.form.HtmlEditor.aclean = [ 
39817     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39818 ];
39819
39820 // protocols..
39821 Roo.form.HtmlEditor.pwhite= [
39822         'http',  'https',  'mailto'
39823 ];
39824
39825 // white listed style attributes.
39826 Roo.form.HtmlEditor.cwhite= [
39827         'text-align',
39828         'font-size'
39829 ];
39830
39831
39832 Roo.form.HtmlEditor.swapCodes   =[ 
39833     [    8211, "--" ], 
39834     [    8212, "--" ], 
39835     [    8216,  "'" ],  
39836     [    8217, "'" ],  
39837     [    8220, '"' ],  
39838     [    8221, '"' ],  
39839     [    8226, "*" ],  
39840     [    8230, "..." ]
39841 ]; 
39842
39843     // <script type="text/javascript">
39844 /*
39845  * Based on
39846  * Ext JS Library 1.1.1
39847  * Copyright(c) 2006-2007, Ext JS, LLC.
39848  *  
39849  
39850  */
39851
39852 /**
39853  * @class Roo.form.HtmlEditorToolbar1
39854  * Basic Toolbar
39855  * 
39856  * Usage:
39857  *
39858  new Roo.form.HtmlEditor({
39859     ....
39860     toolbars : [
39861         new Roo.form.HtmlEditorToolbar1({
39862             disable : { fonts: 1 , format: 1, ..., ... , ...],
39863             btns : [ .... ]
39864         })
39865     }
39866      
39867  * 
39868  * @cfg {Object} disable List of elements to disable..
39869  * @cfg {Array} btns List of additional buttons.
39870  * 
39871  * 
39872  * NEEDS Extra CSS? 
39873  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39874  */
39875  
39876 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39877 {
39878     
39879     Roo.apply(this, config);
39880     
39881     // default disabled, based on 'good practice'..
39882     this.disable = this.disable || {};
39883     Roo.applyIf(this.disable, {
39884         fontSize : true,
39885         colors : true,
39886         specialElements : true
39887     });
39888     
39889     
39890     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39891     // dont call parent... till later.
39892 }
39893
39894 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39895     
39896     tb: false,
39897     
39898     rendered: false,
39899     
39900     editor : false,
39901     /**
39902      * @cfg {Object} disable  List of toolbar elements to disable
39903          
39904      */
39905     disable : false,
39906       /**
39907      * @cfg {Array} fontFamilies An array of available font families
39908      */
39909     fontFamilies : [
39910         'Arial',
39911         'Courier New',
39912         'Tahoma',
39913         'Times New Roman',
39914         'Verdana'
39915     ],
39916     
39917     specialChars : [
39918            "&#169;",
39919           "&#174;",     
39920           "&#8482;",    
39921           "&#163;" ,    
39922          // "&#8212;",    
39923           "&#8230;",    
39924           "&#247;" ,    
39925         //  "&#225;" ,     ?? a acute?
39926            "&#8364;"    , //Euro
39927        //   "&#8220;"    ,
39928         //  "&#8221;"    ,
39929         //  "&#8226;"    ,
39930           "&#176;"  //   , // degrees
39931
39932          // "&#233;"     , // e ecute
39933          // "&#250;"     , // u ecute?
39934     ],
39935     
39936     specialElements : [
39937         {
39938             text: "Insert Table",
39939             xtype: 'MenuItem',
39940             xns : Roo.Menu,
39941             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39942                 
39943         },
39944         {    
39945             text: "Insert Image",
39946             xtype: 'MenuItem',
39947             xns : Roo.Menu,
39948             ihtml : '<img src="about:blank"/>'
39949             
39950         }
39951         
39952          
39953     ],
39954     
39955     
39956     inputElements : [ 
39957             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39958             "input:submit", "input:button", "select", "textarea", "label" ],
39959     formats : [
39960         ["p"] ,  
39961         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39962         ["pre"],[ "code"], 
39963         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39964     ],
39965      /**
39966      * @cfg {String} defaultFont default font to use.
39967      */
39968     defaultFont: 'tahoma',
39969    
39970     fontSelect : false,
39971     
39972     
39973     formatCombo : false,
39974     
39975     init : function(editor)
39976     {
39977         this.editor = editor;
39978         
39979         
39980         var fid = editor.frameId;
39981         var etb = this;
39982         function btn(id, toggle, handler){
39983             var xid = fid + '-'+ id ;
39984             return {
39985                 id : xid,
39986                 cmd : id,
39987                 cls : 'x-btn-icon x-edit-'+id,
39988                 enableToggle:toggle !== false,
39989                 scope: editor, // was editor...
39990                 handler:handler||editor.relayBtnCmd,
39991                 clickEvent:'mousedown',
39992                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39993                 tabIndex:-1
39994             };
39995         }
39996         
39997         
39998         
39999         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40000         this.tb = tb;
40001          // stop form submits
40002         tb.el.on('click', function(e){
40003             e.preventDefault(); // what does this do?
40004         });
40005
40006         if(!this.disable.font && !Roo.isSafari){
40007             /* why no safari for fonts
40008             editor.fontSelect = tb.el.createChild({
40009                 tag:'select',
40010                 tabIndex: -1,
40011                 cls:'x-font-select',
40012                 html: editor.createFontOptions()
40013             });
40014             editor.fontSelect.on('change', function(){
40015                 var font = editor.fontSelect.dom.value;
40016                 editor.relayCmd('fontname', font);
40017                 editor.deferFocus();
40018             }, editor);
40019             tb.add(
40020                 editor.fontSelect.dom,
40021                 '-'
40022             );
40023             */
40024         };
40025         if(!this.disable.formats){
40026             this.formatCombo = new Roo.form.ComboBox({
40027                 store: new Roo.data.SimpleStore({
40028                     id : 'tag',
40029                     fields: ['tag'],
40030                     data : this.formats // from states.js
40031                 }),
40032                 blockFocus : true,
40033                 //autoCreate : {tag: "div",  size: "20"},
40034                 displayField:'tag',
40035                 typeAhead: false,
40036                 mode: 'local',
40037                 editable : false,
40038                 triggerAction: 'all',
40039                 emptyText:'Add tag',
40040                 selectOnFocus:true,
40041                 width:135,
40042                 listeners : {
40043                     'select': function(c, r, i) {
40044                         editor.insertTag(r.get('tag'));
40045                         editor.focus();
40046                     }
40047                 }
40048
40049             });
40050             tb.addField(this.formatCombo);
40051             
40052         }
40053         
40054         if(!this.disable.format){
40055             tb.add(
40056                 btn('bold'),
40057                 btn('italic'),
40058                 btn('underline')
40059             );
40060         };
40061         if(!this.disable.fontSize){
40062             tb.add(
40063                 '-',
40064                 
40065                 
40066                 btn('increasefontsize', false, editor.adjustFont),
40067                 btn('decreasefontsize', false, editor.adjustFont)
40068             );
40069         };
40070         
40071         
40072         if(!this.disable.colors){
40073             tb.add(
40074                 '-', {
40075                     id:editor.frameId +'-forecolor',
40076                     cls:'x-btn-icon x-edit-forecolor',
40077                     clickEvent:'mousedown',
40078                     tooltip: this.buttonTips['forecolor'] || undefined,
40079                     tabIndex:-1,
40080                     menu : new Roo.menu.ColorMenu({
40081                         allowReselect: true,
40082                         focus: Roo.emptyFn,
40083                         value:'000000',
40084                         plain:true,
40085                         selectHandler: function(cp, color){
40086                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40087                             editor.deferFocus();
40088                         },
40089                         scope: editor,
40090                         clickEvent:'mousedown'
40091                     })
40092                 }, {
40093                     id:editor.frameId +'backcolor',
40094                     cls:'x-btn-icon x-edit-backcolor',
40095                     clickEvent:'mousedown',
40096                     tooltip: this.buttonTips['backcolor'] || undefined,
40097                     tabIndex:-1,
40098                     menu : new Roo.menu.ColorMenu({
40099                         focus: Roo.emptyFn,
40100                         value:'FFFFFF',
40101                         plain:true,
40102                         allowReselect: true,
40103                         selectHandler: function(cp, color){
40104                             if(Roo.isGecko){
40105                                 editor.execCmd('useCSS', false);
40106                                 editor.execCmd('hilitecolor', color);
40107                                 editor.execCmd('useCSS', true);
40108                                 editor.deferFocus();
40109                             }else{
40110                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40111                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40112                                 editor.deferFocus();
40113                             }
40114                         },
40115                         scope:editor,
40116                         clickEvent:'mousedown'
40117                     })
40118                 }
40119             );
40120         };
40121         // now add all the items...
40122         
40123
40124         if(!this.disable.alignments){
40125             tb.add(
40126                 '-',
40127                 btn('justifyleft'),
40128                 btn('justifycenter'),
40129                 btn('justifyright')
40130             );
40131         };
40132
40133         //if(!Roo.isSafari){
40134             if(!this.disable.links){
40135                 tb.add(
40136                     '-',
40137                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40138                 );
40139             };
40140
40141             if(!this.disable.lists){
40142                 tb.add(
40143                     '-',
40144                     btn('insertorderedlist'),
40145                     btn('insertunorderedlist')
40146                 );
40147             }
40148             if(!this.disable.sourceEdit){
40149                 tb.add(
40150                     '-',
40151                     btn('sourceedit', true, function(btn){
40152                         this.toggleSourceEdit(btn.pressed);
40153                     })
40154                 );
40155             }
40156         //}
40157         
40158         var smenu = { };
40159         // special menu.. - needs to be tidied up..
40160         if (!this.disable.special) {
40161             smenu = {
40162                 text: "&#169;",
40163                 cls: 'x-edit-none',
40164                 
40165                 menu : {
40166                     items : []
40167                 }
40168             };
40169             for (var i =0; i < this.specialChars.length; i++) {
40170                 smenu.menu.items.push({
40171                     
40172                     html: this.specialChars[i],
40173                     handler: function(a,b) {
40174                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40175                         
40176                     },
40177                     tabIndex:-1
40178                 });
40179             }
40180             
40181             
40182             tb.add(smenu);
40183             
40184             
40185         }
40186          
40187         if (!this.disable.specialElements) {
40188             var semenu = {
40189                 text: "Other;",
40190                 cls: 'x-edit-none',
40191                 menu : {
40192                     items : []
40193                 }
40194             };
40195             for (var i =0; i < this.specialElements.length; i++) {
40196                 semenu.menu.items.push(
40197                     Roo.apply({ 
40198                         handler: function(a,b) {
40199                             editor.insertAtCursor(this.ihtml);
40200                         }
40201                     }, this.specialElements[i])
40202                 );
40203                     
40204             }
40205             
40206             tb.add(semenu);
40207             
40208             
40209         }
40210          
40211         
40212         if (this.btns) {
40213             for(var i =0; i< this.btns.length;i++) {
40214                 var b = this.btns[i];
40215                 b.cls =  'x-edit-none';
40216                 b.scope = editor;
40217                 tb.add(b);
40218             }
40219         
40220         }
40221         
40222         
40223         
40224         // disable everything...
40225         
40226         this.tb.items.each(function(item){
40227            if(item.id != editor.frameId+ '-sourceedit'){
40228                 item.disable();
40229             }
40230         });
40231         this.rendered = true;
40232         
40233         // the all the btns;
40234         editor.on('editorevent', this.updateToolbar, this);
40235         // other toolbars need to implement this..
40236         //editor.on('editmodechange', this.updateToolbar, this);
40237     },
40238     
40239     
40240     
40241     /**
40242      * Protected method that will not generally be called directly. It triggers
40243      * a toolbar update by reading the markup state of the current selection in the editor.
40244      */
40245     updateToolbar: function(){
40246
40247         if(!this.editor.activated){
40248             this.editor.onFirstFocus();
40249             return;
40250         }
40251
40252         var btns = this.tb.items.map, 
40253             doc = this.editor.doc,
40254             frameId = this.editor.frameId;
40255
40256         if(!this.disable.font && !Roo.isSafari){
40257             /*
40258             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40259             if(name != this.fontSelect.dom.value){
40260                 this.fontSelect.dom.value = name;
40261             }
40262             */
40263         }
40264         if(!this.disable.format){
40265             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40266             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40267             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40268         }
40269         if(!this.disable.alignments){
40270             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40271             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40272             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40273         }
40274         if(!Roo.isSafari && !this.disable.lists){
40275             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40276             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40277         }
40278         
40279         var ans = this.editor.getAllAncestors();
40280         if (this.formatCombo) {
40281             
40282             
40283             var store = this.formatCombo.store;
40284             this.formatCombo.setValue("");
40285             for (var i =0; i < ans.length;i++) {
40286                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40287                     // select it..
40288                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40289                     break;
40290                 }
40291             }
40292         }
40293         
40294         
40295         
40296         // hides menus... - so this cant be on a menu...
40297         Roo.menu.MenuMgr.hideAll();
40298
40299         //this.editorsyncValue();
40300     },
40301    
40302     
40303     createFontOptions : function(){
40304         var buf = [], fs = this.fontFamilies, ff, lc;
40305         for(var i = 0, len = fs.length; i< len; i++){
40306             ff = fs[i];
40307             lc = ff.toLowerCase();
40308             buf.push(
40309                 '<option value="',lc,'" style="font-family:',ff,';"',
40310                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40311                     ff,
40312                 '</option>'
40313             );
40314         }
40315         return buf.join('');
40316     },
40317     
40318     toggleSourceEdit : function(sourceEditMode){
40319         if(sourceEditMode === undefined){
40320             sourceEditMode = !this.sourceEditMode;
40321         }
40322         this.sourceEditMode = sourceEditMode === true;
40323         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40324         // just toggle the button?
40325         if(btn.pressed !== this.editor.sourceEditMode){
40326             btn.toggle(this.editor.sourceEditMode);
40327             return;
40328         }
40329         
40330         if(this.sourceEditMode){
40331             this.tb.items.each(function(item){
40332                 if(item.cmd != 'sourceedit'){
40333                     item.disable();
40334                 }
40335             });
40336           
40337         }else{
40338             if(this.initialized){
40339                 this.tb.items.each(function(item){
40340                     item.enable();
40341                 });
40342             }
40343             
40344         }
40345         // tell the editor that it's been pressed..
40346         this.editor.toggleSourceEdit(sourceEditMode);
40347        
40348     },
40349      /**
40350      * Object collection of toolbar tooltips for the buttons in the editor. The key
40351      * is the command id associated with that button and the value is a valid QuickTips object.
40352      * For example:
40353 <pre><code>
40354 {
40355     bold : {
40356         title: 'Bold (Ctrl+B)',
40357         text: 'Make the selected text bold.',
40358         cls: 'x-html-editor-tip'
40359     },
40360     italic : {
40361         title: 'Italic (Ctrl+I)',
40362         text: 'Make the selected text italic.',
40363         cls: 'x-html-editor-tip'
40364     },
40365     ...
40366 </code></pre>
40367     * @type Object
40368      */
40369     buttonTips : {
40370         bold : {
40371             title: 'Bold (Ctrl+B)',
40372             text: 'Make the selected text bold.',
40373             cls: 'x-html-editor-tip'
40374         },
40375         italic : {
40376             title: 'Italic (Ctrl+I)',
40377             text: 'Make the selected text italic.',
40378             cls: 'x-html-editor-tip'
40379         },
40380         underline : {
40381             title: 'Underline (Ctrl+U)',
40382             text: 'Underline the selected text.',
40383             cls: 'x-html-editor-tip'
40384         },
40385         increasefontsize : {
40386             title: 'Grow Text',
40387             text: 'Increase the font size.',
40388             cls: 'x-html-editor-tip'
40389         },
40390         decreasefontsize : {
40391             title: 'Shrink Text',
40392             text: 'Decrease the font size.',
40393             cls: 'x-html-editor-tip'
40394         },
40395         backcolor : {
40396             title: 'Text Highlight Color',
40397             text: 'Change the background color of the selected text.',
40398             cls: 'x-html-editor-tip'
40399         },
40400         forecolor : {
40401             title: 'Font Color',
40402             text: 'Change the color of the selected text.',
40403             cls: 'x-html-editor-tip'
40404         },
40405         justifyleft : {
40406             title: 'Align Text Left',
40407             text: 'Align text to the left.',
40408             cls: 'x-html-editor-tip'
40409         },
40410         justifycenter : {
40411             title: 'Center Text',
40412             text: 'Center text in the editor.',
40413             cls: 'x-html-editor-tip'
40414         },
40415         justifyright : {
40416             title: 'Align Text Right',
40417             text: 'Align text to the right.',
40418             cls: 'x-html-editor-tip'
40419         },
40420         insertunorderedlist : {
40421             title: 'Bullet List',
40422             text: 'Start a bulleted list.',
40423             cls: 'x-html-editor-tip'
40424         },
40425         insertorderedlist : {
40426             title: 'Numbered List',
40427             text: 'Start a numbered list.',
40428             cls: 'x-html-editor-tip'
40429         },
40430         createlink : {
40431             title: 'Hyperlink',
40432             text: 'Make the selected text a hyperlink.',
40433             cls: 'x-html-editor-tip'
40434         },
40435         sourceedit : {
40436             title: 'Source Edit',
40437             text: 'Switch to source editing mode.',
40438             cls: 'x-html-editor-tip'
40439         }
40440     },
40441     // private
40442     onDestroy : function(){
40443         if(this.rendered){
40444             
40445             this.tb.items.each(function(item){
40446                 if(item.menu){
40447                     item.menu.removeAll();
40448                     if(item.menu.el){
40449                         item.menu.el.destroy();
40450                     }
40451                 }
40452                 item.destroy();
40453             });
40454              
40455         }
40456     },
40457     onFirstFocus: function() {
40458         this.tb.items.each(function(item){
40459            item.enable();
40460         });
40461     }
40462 });
40463
40464
40465
40466
40467 // <script type="text/javascript">
40468 /*
40469  * Based on
40470  * Ext JS Library 1.1.1
40471  * Copyright(c) 2006-2007, Ext JS, LLC.
40472  *  
40473  
40474  */
40475
40476  
40477 /**
40478  * @class Roo.form.HtmlEditor.ToolbarContext
40479  * Context Toolbar
40480  * 
40481  * Usage:
40482  *
40483  new Roo.form.HtmlEditor({
40484     ....
40485     toolbars : [
40486         { xtype: 'ToolbarStandard', styles : {} }
40487         { xtype: 'ToolbarContext', disable : {} }
40488     ]
40489 })
40490
40491      
40492  * 
40493  * @config : {Object} disable List of elements to disable.. (not done yet.)
40494  * @config : {Object} styles  Map of styles available.
40495  * 
40496  */
40497
40498 Roo.form.HtmlEditor.ToolbarContext = function(config)
40499 {
40500     
40501     Roo.apply(this, config);
40502     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40503     // dont call parent... till later.
40504     this.styles = this.styles || {};
40505 }
40506 Roo.form.HtmlEditor.ToolbarContext.types = {
40507     'IMG' : {
40508         width : {
40509             title: "Width",
40510             width: 40
40511         },
40512         height:  {
40513             title: "Height",
40514             width: 40
40515         },
40516         align: {
40517             title: "Align",
40518             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40519             width : 80
40520             
40521         },
40522         border: {
40523             title: "Border",
40524             width: 40
40525         },
40526         alt: {
40527             title: "Alt",
40528             width: 120
40529         },
40530         src : {
40531             title: "Src",
40532             width: 220
40533         }
40534         
40535     },
40536     'A' : {
40537         name : {
40538             title: "Name",
40539             width: 50
40540         },
40541         href:  {
40542             title: "Href",
40543             width: 220
40544         } // border?
40545         
40546     },
40547     'TABLE' : {
40548         rows : {
40549             title: "Rows",
40550             width: 20
40551         },
40552         cols : {
40553             title: "Cols",
40554             width: 20
40555         },
40556         width : {
40557             title: "Width",
40558             width: 40
40559         },
40560         height : {
40561             title: "Height",
40562             width: 40
40563         },
40564         border : {
40565             title: "Border",
40566             width: 20
40567         }
40568     },
40569     'TD' : {
40570         width : {
40571             title: "Width",
40572             width: 40
40573         },
40574         height : {
40575             title: "Height",
40576             width: 40
40577         },   
40578         align: {
40579             title: "Align",
40580             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40581             width: 80
40582         },
40583         valign: {
40584             title: "Valign",
40585             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40586             width: 80
40587         },
40588         colspan: {
40589             title: "Colspan",
40590             width: 20
40591             
40592         }
40593     },
40594     'INPUT' : {
40595         name : {
40596             title: "name",
40597             width: 120
40598         },
40599         value : {
40600             title: "Value",
40601             width: 120
40602         },
40603         width : {
40604             title: "Width",
40605             width: 40
40606         }
40607     },
40608     'LABEL' : {
40609         'for' : {
40610             title: "For",
40611             width: 120
40612         }
40613     },
40614     'TEXTAREA' : {
40615           name : {
40616             title: "name",
40617             width: 120
40618         },
40619         rows : {
40620             title: "Rows",
40621             width: 20
40622         },
40623         cols : {
40624             title: "Cols",
40625             width: 20
40626         }
40627     },
40628     'SELECT' : {
40629         name : {
40630             title: "name",
40631             width: 120
40632         },
40633         selectoptions : {
40634             title: "Options",
40635             width: 200
40636         }
40637     },
40638     
40639     // should we really allow this??
40640     // should this just be 
40641     'BODY' : {
40642         title : {
40643             title: "title",
40644             width: 200,
40645             disabled : true
40646         }
40647     },
40648     '*' : {
40649         // empty..
40650     }
40651 };
40652
40653
40654
40655 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40656     
40657     tb: false,
40658     
40659     rendered: false,
40660     
40661     editor : false,
40662     /**
40663      * @cfg {Object} disable  List of toolbar elements to disable
40664          
40665      */
40666     disable : false,
40667     /**
40668      * @cfg {Object} styles List of styles 
40669      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40670      *
40671      * These must be defined in the page, so they get rendered correctly..
40672      * .headline { }
40673      * TD.underline { }
40674      * 
40675      */
40676     styles : false,
40677     
40678     
40679     
40680     toolbars : false,
40681     
40682     init : function(editor)
40683     {
40684         this.editor = editor;
40685         
40686         
40687         var fid = editor.frameId;
40688         var etb = this;
40689         function btn(id, toggle, handler){
40690             var xid = fid + '-'+ id ;
40691             return {
40692                 id : xid,
40693                 cmd : id,
40694                 cls : 'x-btn-icon x-edit-'+id,
40695                 enableToggle:toggle !== false,
40696                 scope: editor, // was editor...
40697                 handler:handler||editor.relayBtnCmd,
40698                 clickEvent:'mousedown',
40699                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40700                 tabIndex:-1
40701             };
40702         }
40703         // create a new element.
40704         var wdiv = editor.wrap.createChild({
40705                 tag: 'div'
40706             }, editor.wrap.dom.firstChild.nextSibling, true);
40707         
40708         // can we do this more than once??
40709         
40710          // stop form submits
40711       
40712  
40713         // disable everything...
40714         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40715         this.toolbars = {};
40716            
40717         for (var i in  ty) {
40718           
40719             this.toolbars[i] = this.buildToolbar(ty[i],i);
40720         }
40721         this.tb = this.toolbars.BODY;
40722         this.tb.el.show();
40723         this.buildFooter();
40724         this.footer.show();
40725          
40726         this.rendered = true;
40727         
40728         // the all the btns;
40729         editor.on('editorevent', this.updateToolbar, this);
40730         // other toolbars need to implement this..
40731         //editor.on('editmodechange', this.updateToolbar, this);
40732     },
40733     
40734     
40735     
40736     /**
40737      * Protected method that will not generally be called directly. It triggers
40738      * a toolbar update by reading the markup state of the current selection in the editor.
40739      */
40740     updateToolbar: function(ignore_a,ignore_b,sel){
40741
40742         
40743         if(!this.editor.activated){
40744              this.editor.onFirstFocus();
40745             return;
40746         }
40747         var updateFooter = sel ? false : true;
40748         
40749         
40750         var ans = this.editor.getAllAncestors();
40751         
40752         // pick
40753         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40754         
40755         if (!sel) { 
40756             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40757             sel = sel ? sel : this.editor.doc.body;
40758             sel = sel.tagName.length ? sel : this.editor.doc.body;
40759             
40760         }
40761         // pick a menu that exists..
40762         var tn = sel.tagName.toUpperCase();
40763         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40764         
40765         tn = sel.tagName.toUpperCase();
40766         
40767         var lastSel = this.tb.selectedNode
40768         
40769         this.tb.selectedNode = sel;
40770         
40771         // if current menu does not match..
40772         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40773                 
40774             this.tb.el.hide();
40775             ///console.log("show: " + tn);
40776             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40777             this.tb.el.show();
40778             // update name
40779             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40780             
40781             
40782             // update attributes
40783             if (this.tb.fields) {
40784                 this.tb.fields.each(function(e) {
40785                    e.setValue(sel.getAttribute(e.name));
40786                 });
40787             }
40788             
40789             // update styles
40790             var st = this.tb.fields.item(0);
40791             st.store.removeAll();
40792             var cn = sel.className.split(/\s+/);
40793             
40794             var avs = [];
40795             if (this.styles['*']) {
40796                 
40797                 Roo.each(this.styles['*'], function(v) {
40798                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40799                 });
40800             }
40801             if (this.styles[tn]) { 
40802                 Roo.each(this.styles[tn], function(v) {
40803                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40804                 });
40805             }
40806             
40807             st.store.loadData(avs);
40808             st.collapse();
40809             st.setValue(cn);
40810             
40811             // flag our selected Node.
40812             this.tb.selectedNode = sel;
40813            
40814            
40815             Roo.menu.MenuMgr.hideAll();
40816
40817         }
40818         
40819         if (!updateFooter) {
40820             return;
40821         }
40822         // update the footer
40823         //
40824         var html = '';
40825         
40826         this.footerEls = ans.reverse();
40827         Roo.each(this.footerEls, function(a,i) {
40828             if (!a) { return; }
40829             html += html.length ? ' &gt; '  :  '';
40830             
40831             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40832             
40833         });
40834        
40835         // 
40836         var sz = this.footDisp.up('td').getSize();
40837         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40838         this.footDisp.dom.style.marginLeft = '5px';
40839         
40840         this.footDisp.dom.style.overflow = 'hidden';
40841         
40842         this.footDisp.dom.innerHTML = html;
40843             
40844         //this.editorsyncValue();
40845     },
40846    
40847        
40848     // private
40849     onDestroy : function(){
40850         if(this.rendered){
40851             
40852             this.tb.items.each(function(item){
40853                 if(item.menu){
40854                     item.menu.removeAll();
40855                     if(item.menu.el){
40856                         item.menu.el.destroy();
40857                     }
40858                 }
40859                 item.destroy();
40860             });
40861              
40862         }
40863     },
40864     onFirstFocus: function() {
40865         // need to do this for all the toolbars..
40866         this.tb.items.each(function(item){
40867            item.enable();
40868         });
40869     },
40870     buildToolbar: function(tlist, nm)
40871     {
40872         var editor = this.editor;
40873          // create a new element.
40874         var wdiv = editor.wrap.createChild({
40875                 tag: 'div'
40876             }, editor.wrap.dom.firstChild.nextSibling, true);
40877         
40878        
40879         var tb = new Roo.Toolbar(wdiv);
40880         // add the name..
40881         
40882         tb.add(nm+ ":&nbsp;");
40883         
40884         // styles...
40885         if (this.styles) {
40886             
40887             // this needs a multi-select checkbox...
40888             tb.addField( new Roo.form.ComboBox({
40889                 store: new Roo.data.SimpleStore({
40890                     id : 'val',
40891                     fields: ['val', 'selected'],
40892                     data : [] 
40893                 }),
40894                 name : 'className',
40895                 displayField:'val',
40896                 typeAhead: false,
40897                 mode: 'local',
40898                 editable : false,
40899                 triggerAction: 'all',
40900                 emptyText:'Select Style',
40901                 selectOnFocus:true,
40902                 width: 130,
40903                 listeners : {
40904                     'select': function(c, r, i) {
40905                         // initial support only for on class per el..
40906                         tb.selectedNode.className =  r ? r.get('val') : '';
40907                     }
40908                 }
40909     
40910             }));
40911         }
40912             
40913         
40914         
40915         for (var i in tlist) {
40916             
40917             var item = tlist[i];
40918             tb.add(item.title + ":&nbsp;");
40919             
40920             
40921             
40922             
40923             if (item.opts) {
40924                 // opts == pulldown..
40925                 tb.addField( new Roo.form.ComboBox({
40926                     store: new Roo.data.SimpleStore({
40927                         id : 'val',
40928                         fields: ['val'],
40929                         data : item.opts  
40930                     }),
40931                     name : i,
40932                     displayField:'val',
40933                     typeAhead: false,
40934                     mode: 'local',
40935                     editable : false,
40936                     triggerAction: 'all',
40937                     emptyText:'Select',
40938                     selectOnFocus:true,
40939                     width: item.width ? item.width  : 130,
40940                     listeners : {
40941                         'select': function(c, r, i) {
40942                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40943                         }
40944                     }
40945
40946                 }));
40947                 continue;
40948                     
40949                  
40950                 
40951                 tb.addField( new Roo.form.TextField({
40952                     name: i,
40953                     width: 100,
40954                     //allowBlank:false,
40955                     value: ''
40956                 }));
40957                 continue;
40958             }
40959             tb.addField( new Roo.form.TextField({
40960                 name: i,
40961                 width: item.width,
40962                 //allowBlank:true,
40963                 value: '',
40964                 listeners: {
40965                     'change' : function(f, nv, ov) {
40966                         tb.selectedNode.setAttribute(f.name, nv);
40967                     }
40968                 }
40969             }));
40970              
40971         }
40972         tb.el.on('click', function(e){
40973             e.preventDefault(); // what does this do?
40974         });
40975         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40976         tb.el.hide();
40977         tb.name = nm;
40978         // dont need to disable them... as they will get hidden
40979         return tb;
40980          
40981         
40982     },
40983     buildFooter : function()
40984     {
40985         
40986         var fel = this.editor.wrap.createChild();
40987         this.footer = new Roo.Toolbar(fel);
40988         // toolbar has scrolly on left / right?
40989         var footDisp= new Roo.Toolbar.Fill();
40990         var _t = this;
40991         this.footer.add(
40992             {
40993                 text : '&lt;',
40994                 xtype: 'Button',
40995                 handler : function() {
40996                     _t.footDisp.scrollTo('left',0,true)
40997                 }
40998             }
40999         );
41000         this.footer.add( footDisp );
41001         this.footer.add( 
41002             {
41003                 text : '&gt;',
41004                 xtype: 'Button',
41005                 handler : function() {
41006                     // no animation..
41007                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41008                 }
41009             }
41010         );
41011         var fel = Roo.get(footDisp.el);
41012         fel.addClass('x-editor-context');
41013         this.footDispWrap = fel; 
41014         this.footDispWrap.overflow  = 'hidden';
41015         
41016         this.footDisp = fel.createChild();
41017         this.footDispWrap.on('click', this.onContextClick, this)
41018         
41019         
41020     },
41021     onContextClick : function (ev,dom)
41022     {
41023         ev.preventDefault();
41024         var  cn = dom.className;
41025         Roo.log(cn);
41026         if (!cn.match(/x-ed-loc-/)) {
41027             return;
41028         }
41029         var n = cn.split('-').pop();
41030         var ans = this.footerEls;
41031         var sel = ans[n];
41032         
41033          // pick
41034         var range = this.editor.createRange();
41035         
41036         range.selectNodeContents(sel);
41037         //range.selectNode(sel);
41038         
41039         
41040         var selection = this.editor.getSelection();
41041         selection.removeAllRanges();
41042         selection.addRange(range);
41043         
41044         
41045         
41046         this.updateToolbar(null, null, sel);
41047         
41048         
41049     }
41050     
41051     
41052     
41053     
41054     
41055 });
41056
41057
41058
41059
41060
41061 /*
41062  * Based on:
41063  * Ext JS Library 1.1.1
41064  * Copyright(c) 2006-2007, Ext JS, LLC.
41065  *
41066  * Originally Released Under LGPL - original licence link has changed is not relivant.
41067  *
41068  * Fork - LGPL
41069  * <script type="text/javascript">
41070  */
41071  
41072 /**
41073  * @class Roo.form.BasicForm
41074  * @extends Roo.util.Observable
41075  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41076  * @constructor
41077  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41078  * @param {Object} config Configuration options
41079  */
41080 Roo.form.BasicForm = function(el, config){
41081     this.allItems = [];
41082     this.childForms = [];
41083     Roo.apply(this, config);
41084     /*
41085      * The Roo.form.Field items in this form.
41086      * @type MixedCollection
41087      */
41088      
41089      
41090     this.items = new Roo.util.MixedCollection(false, function(o){
41091         return o.id || (o.id = Roo.id());
41092     });
41093     this.addEvents({
41094         /**
41095          * @event beforeaction
41096          * Fires before any action is performed. Return false to cancel the action.
41097          * @param {Form} this
41098          * @param {Action} action The action to be performed
41099          */
41100         beforeaction: true,
41101         /**
41102          * @event actionfailed
41103          * Fires when an action fails.
41104          * @param {Form} this
41105          * @param {Action} action The action that failed
41106          */
41107         actionfailed : true,
41108         /**
41109          * @event actioncomplete
41110          * Fires when an action is completed.
41111          * @param {Form} this
41112          * @param {Action} action The action that completed
41113          */
41114         actioncomplete : true
41115     });
41116     if(el){
41117         this.initEl(el);
41118     }
41119     Roo.form.BasicForm.superclass.constructor.call(this);
41120 };
41121
41122 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41123     /**
41124      * @cfg {String} method
41125      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41126      */
41127     /**
41128      * @cfg {DataReader} reader
41129      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41130      * This is optional as there is built-in support for processing JSON.
41131      */
41132     /**
41133      * @cfg {DataReader} errorReader
41134      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41135      * This is completely optional as there is built-in support for processing JSON.
41136      */
41137     /**
41138      * @cfg {String} url
41139      * The URL to use for form actions if one isn't supplied in the action options.
41140      */
41141     /**
41142      * @cfg {Boolean} fileUpload
41143      * Set to true if this form is a file upload.
41144      */
41145      
41146     /**
41147      * @cfg {Object} baseParams
41148      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41149      */
41150      /**
41151      
41152     /**
41153      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41154      */
41155     timeout: 30,
41156
41157     // private
41158     activeAction : null,
41159
41160     /**
41161      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41162      * or setValues() data instead of when the form was first created.
41163      */
41164     trackResetOnLoad : false,
41165     
41166     
41167     /**
41168      * childForms - used for multi-tab forms
41169      * @type {Array}
41170      */
41171     childForms : false,
41172     
41173     /**
41174      * allItems - full list of fields.
41175      * @type {Array}
41176      */
41177     allItems : false,
41178     
41179     /**
41180      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41181      * element by passing it or its id or mask the form itself by passing in true.
41182      * @type Mixed
41183      */
41184     waitMsgTarget : false,
41185
41186     // private
41187     initEl : function(el){
41188         this.el = Roo.get(el);
41189         this.id = this.el.id || Roo.id();
41190         this.el.on('submit', this.onSubmit, this);
41191         this.el.addClass('x-form');
41192     },
41193
41194     // private
41195     onSubmit : function(e){
41196         e.stopEvent();
41197     },
41198
41199     /**
41200      * Returns true if client-side validation on the form is successful.
41201      * @return Boolean
41202      */
41203     isValid : function(){
41204         var valid = true;
41205         this.items.each(function(f){
41206            if(!f.validate()){
41207                valid = false;
41208            }
41209         });
41210         return valid;
41211     },
41212
41213     /**
41214      * Returns true if any fields in this form have changed since their original load.
41215      * @return Boolean
41216      */
41217     isDirty : function(){
41218         var dirty = false;
41219         this.items.each(function(f){
41220            if(f.isDirty()){
41221                dirty = true;
41222                return false;
41223            }
41224         });
41225         return dirty;
41226     },
41227
41228     /**
41229      * Performs a predefined action (submit or load) or custom actions you define on this form.
41230      * @param {String} actionName The name of the action type
41231      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41232      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41233      * accept other config options):
41234      * <pre>
41235 Property          Type             Description
41236 ----------------  ---------------  ----------------------------------------------------------------------------------
41237 url               String           The url for the action (defaults to the form's url)
41238 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41239 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41240 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41241                                    validate the form on the client (defaults to false)
41242      * </pre>
41243      * @return {BasicForm} this
41244      */
41245     doAction : function(action, options){
41246         if(typeof action == 'string'){
41247             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41248         }
41249         if(this.fireEvent('beforeaction', this, action) !== false){
41250             this.beforeAction(action);
41251             action.run.defer(100, action);
41252         }
41253         return this;
41254     },
41255
41256     /**
41257      * Shortcut to do a submit action.
41258      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41259      * @return {BasicForm} this
41260      */
41261     submit : function(options){
41262         this.doAction('submit', options);
41263         return this;
41264     },
41265
41266     /**
41267      * Shortcut to do a load action.
41268      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41269      * @return {BasicForm} this
41270      */
41271     load : function(options){
41272         this.doAction('load', options);
41273         return this;
41274     },
41275
41276     /**
41277      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41278      * @param {Record} record The record to edit
41279      * @return {BasicForm} this
41280      */
41281     updateRecord : function(record){
41282         record.beginEdit();
41283         var fs = record.fields;
41284         fs.each(function(f){
41285             var field = this.findField(f.name);
41286             if(field){
41287                 record.set(f.name, field.getValue());
41288             }
41289         }, this);
41290         record.endEdit();
41291         return this;
41292     },
41293
41294     /**
41295      * Loads an Roo.data.Record into this form.
41296      * @param {Record} record The record to load
41297      * @return {BasicForm} this
41298      */
41299     loadRecord : function(record){
41300         this.setValues(record.data);
41301         return this;
41302     },
41303
41304     // private
41305     beforeAction : function(action){
41306         var o = action.options;
41307         
41308        
41309         if(this.waitMsgTarget === true){
41310             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41311         }else if(this.waitMsgTarget){
41312             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41313             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41314         }else {
41315             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41316         }
41317          
41318     },
41319
41320     // private
41321     afterAction : function(action, success){
41322         this.activeAction = null;
41323         var o = action.options;
41324         
41325         if(this.waitMsgTarget === true){
41326             this.el.unmask();
41327         }else if(this.waitMsgTarget){
41328             this.waitMsgTarget.unmask();
41329         }else{
41330             Roo.MessageBox.updateProgress(1);
41331             Roo.MessageBox.hide();
41332         }
41333          
41334         if(success){
41335             if(o.reset){
41336                 this.reset();
41337             }
41338             Roo.callback(o.success, o.scope, [this, action]);
41339             this.fireEvent('actioncomplete', this, action);
41340             
41341         }else{
41342             Roo.callback(o.failure, o.scope, [this, action]);
41343             // show an error message if no failed handler is set..
41344             if (!this.hasListener('actionfailed')) {
41345                 Roo.MessageBox.alert("Error",
41346                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41347                         action.result.errorMsg :
41348                         "Saving Failed, please check your entries"
41349                 );
41350             }
41351             
41352             this.fireEvent('actionfailed', this, action);
41353         }
41354         
41355     },
41356
41357     /**
41358      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41359      * @param {String} id The value to search for
41360      * @return Field
41361      */
41362     findField : function(id){
41363         var field = this.items.get(id);
41364         if(!field){
41365             this.items.each(function(f){
41366                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41367                     field = f;
41368                     return false;
41369                 }
41370             });
41371         }
41372         return field || null;
41373     },
41374
41375     /**
41376      * Add a secondary form to this one, 
41377      * Used to provide tabbed forms. One form is primary, with hidden values 
41378      * which mirror the elements from the other forms.
41379      * 
41380      * @param {Roo.form.Form} form to add.
41381      * 
41382      */
41383     addForm : function(form)
41384     {
41385        
41386         if (this.childForms.indexOf(form) > -1) {
41387             // already added..
41388             return;
41389         }
41390         this.childForms.push(form);
41391         var n = '';
41392         Roo.each(form.allItems, function (fe) {
41393             
41394             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41395             if (this.findField(n)) { // already added..
41396                 return;
41397             }
41398             var add = new Roo.form.Hidden({
41399                 name : n
41400             });
41401             add.render(this.el);
41402             
41403             this.add( add );
41404         }, this);
41405         
41406     },
41407     /**
41408      * Mark fields in this form invalid in bulk.
41409      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41410      * @return {BasicForm} this
41411      */
41412     markInvalid : function(errors){
41413         if(errors instanceof Array){
41414             for(var i = 0, len = errors.length; i < len; i++){
41415                 var fieldError = errors[i];
41416                 var f = this.findField(fieldError.id);
41417                 if(f){
41418                     f.markInvalid(fieldError.msg);
41419                 }
41420             }
41421         }else{
41422             var field, id;
41423             for(id in errors){
41424                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41425                     field.markInvalid(errors[id]);
41426                 }
41427             }
41428         }
41429         Roo.each(this.childForms || [], function (f) {
41430             f.markInvalid(errors);
41431         });
41432         
41433         return this;
41434     },
41435
41436     /**
41437      * Set values for fields in this form in bulk.
41438      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41439      * @return {BasicForm} this
41440      */
41441     setValues : function(values){
41442         if(values instanceof Array){ // array of objects
41443             for(var i = 0, len = values.length; i < len; i++){
41444                 var v = values[i];
41445                 var f = this.findField(v.id);
41446                 if(f){
41447                     f.setValue(v.value);
41448                     if(this.trackResetOnLoad){
41449                         f.originalValue = f.getValue();
41450                     }
41451                 }
41452             }
41453         }else{ // object hash
41454             var field, id;
41455             for(id in values){
41456                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41457                     
41458                     if (field.setFromData && 
41459                         field.valueField && 
41460                         field.displayField &&
41461                         // combos' with local stores can 
41462                         // be queried via setValue()
41463                         // to set their value..
41464                         (field.store && !field.store.isLocal)
41465                         ) {
41466                         // it's a combo
41467                         var sd = { };
41468                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41469                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41470                         field.setFromData(sd);
41471                         
41472                     } else {
41473                         field.setValue(values[id]);
41474                     }
41475                     
41476                     
41477                     if(this.trackResetOnLoad){
41478                         field.originalValue = field.getValue();
41479                     }
41480                 }
41481             }
41482         }
41483          
41484         Roo.each(this.childForms || [], function (f) {
41485             f.setValues(values);
41486         });
41487                 
41488         return this;
41489     },
41490
41491     /**
41492      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41493      * they are returned as an array.
41494      * @param {Boolean} asString
41495      * @return {Object}
41496      */
41497     getValues : function(asString){
41498         if (this.childForms) {
41499             // copy values from the child forms
41500             Roo.each(this.childForms, function (f) {
41501                 this.setValues(f.getValues());
41502             }, this);
41503         }
41504         
41505         
41506         
41507         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41508         if(asString === true){
41509             return fs;
41510         }
41511         return Roo.urlDecode(fs);
41512     },
41513     
41514     /**
41515      * Returns the fields in this form as an object with key/value pairs. 
41516      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41517      * @return {Object}
41518      */
41519     getFieldValues : function(with_hidden)
41520     {
41521         if (this.childForms) {
41522             // copy values from the child forms
41523             // should this call getFieldValues - probably not as we do not currently copy
41524             // hidden fields when we generate..
41525             Roo.each(this.childForms, function (f) {
41526                 this.setValues(f.getValues());
41527             }, this);
41528         }
41529         
41530         var ret = {};
41531         this.items.each(function(f){
41532             if (!f.getName()) {
41533                 return;
41534             }
41535             var v = f.getValue();
41536             // not sure if this supported any more..
41537             if ((typeof(v) == 'object') && f.getRawValue) {
41538                 v = f.getRawValue() ; // dates..
41539             }
41540             // combo boxes where name != hiddenName...
41541             if (f.name != f.getName()) {
41542                 ret[f.name] = f.getRawValue();
41543             }
41544             ret[f.getName()] = v;
41545         });
41546         
41547         return ret;
41548     },
41549
41550     /**
41551      * Clears all invalid messages in this form.
41552      * @return {BasicForm} this
41553      */
41554     clearInvalid : function(){
41555         this.items.each(function(f){
41556            f.clearInvalid();
41557         });
41558         
41559         Roo.each(this.childForms || [], function (f) {
41560             f.clearInvalid();
41561         });
41562         
41563         
41564         return this;
41565     },
41566
41567     /**
41568      * Resets this form.
41569      * @return {BasicForm} this
41570      */
41571     reset : function(){
41572         this.items.each(function(f){
41573             f.reset();
41574         });
41575         
41576         Roo.each(this.childForms || [], function (f) {
41577             f.reset();
41578         });
41579        
41580         
41581         return this;
41582     },
41583
41584     /**
41585      * Add Roo.form components to this form.
41586      * @param {Field} field1
41587      * @param {Field} field2 (optional)
41588      * @param {Field} etc (optional)
41589      * @return {BasicForm} this
41590      */
41591     add : function(){
41592         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41593         return this;
41594     },
41595
41596
41597     /**
41598      * Removes a field from the items collection (does NOT remove its markup).
41599      * @param {Field} field
41600      * @return {BasicForm} this
41601      */
41602     remove : function(field){
41603         this.items.remove(field);
41604         return this;
41605     },
41606
41607     /**
41608      * Looks at the fields in this form, checks them for an id attribute,
41609      * and calls applyTo on the existing dom element with that id.
41610      * @return {BasicForm} this
41611      */
41612     render : function(){
41613         this.items.each(function(f){
41614             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41615                 f.applyTo(f.id);
41616             }
41617         });
41618         return this;
41619     },
41620
41621     /**
41622      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41623      * @param {Object} values
41624      * @return {BasicForm} this
41625      */
41626     applyToFields : function(o){
41627         this.items.each(function(f){
41628            Roo.apply(f, o);
41629         });
41630         return this;
41631     },
41632
41633     /**
41634      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41635      * @param {Object} values
41636      * @return {BasicForm} this
41637      */
41638     applyIfToFields : function(o){
41639         this.items.each(function(f){
41640            Roo.applyIf(f, o);
41641         });
41642         return this;
41643     }
41644 });
41645
41646 // back compat
41647 Roo.BasicForm = Roo.form.BasicForm;/*
41648  * Based on:
41649  * Ext JS Library 1.1.1
41650  * Copyright(c) 2006-2007, Ext JS, LLC.
41651  *
41652  * Originally Released Under LGPL - original licence link has changed is not relivant.
41653  *
41654  * Fork - LGPL
41655  * <script type="text/javascript">
41656  */
41657
41658 /**
41659  * @class Roo.form.Form
41660  * @extends Roo.form.BasicForm
41661  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41662  * @constructor
41663  * @param {Object} config Configuration options
41664  */
41665 Roo.form.Form = function(config){
41666     var xitems =  [];
41667     if (config.items) {
41668         xitems = config.items;
41669         delete config.items;
41670     }
41671    
41672     
41673     Roo.form.Form.superclass.constructor.call(this, null, config);
41674     this.url = this.url || this.action;
41675     if(!this.root){
41676         this.root = new Roo.form.Layout(Roo.applyIf({
41677             id: Roo.id()
41678         }, config));
41679     }
41680     this.active = this.root;
41681     /**
41682      * Array of all the buttons that have been added to this form via {@link addButton}
41683      * @type Array
41684      */
41685     this.buttons = [];
41686     this.allItems = [];
41687     this.addEvents({
41688         /**
41689          * @event clientvalidation
41690          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41691          * @param {Form} this
41692          * @param {Boolean} valid true if the form has passed client-side validation
41693          */
41694         clientvalidation: true,
41695         /**
41696          * @event rendered
41697          * Fires when the form is rendered
41698          * @param {Roo.form.Form} form
41699          */
41700         rendered : true
41701     });
41702     
41703     if (this.progressUrl) {
41704             // push a hidden field onto the list of fields..
41705             this.addxtype( {
41706                     xns: Roo.form, 
41707                     xtype : 'Hidden', 
41708                     name : 'UPLOAD_IDENTIFIER' 
41709             });
41710         }
41711         
41712     
41713     Roo.each(xitems, this.addxtype, this);
41714     
41715     
41716     
41717 };
41718
41719 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41720     /**
41721      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41722      */
41723     /**
41724      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41725      */
41726     /**
41727      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41728      */
41729     buttonAlign:'center',
41730
41731     /**
41732      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41733      */
41734     minButtonWidth:75,
41735
41736     /**
41737      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41738      * This property cascades to child containers if not set.
41739      */
41740     labelAlign:'left',
41741
41742     /**
41743      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41744      * fires a looping event with that state. This is required to bind buttons to the valid
41745      * state using the config value formBind:true on the button.
41746      */
41747     monitorValid : false,
41748
41749     /**
41750      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41751      */
41752     monitorPoll : 200,
41753     
41754     /**
41755      * @cfg {String} progressUrl - Url to return progress data 
41756      */
41757     
41758     progressUrl : false,
41759   
41760     /**
41761      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41762      * fields are added and the column is closed. If no fields are passed the column remains open
41763      * until end() is called.
41764      * @param {Object} config The config to pass to the column
41765      * @param {Field} field1 (optional)
41766      * @param {Field} field2 (optional)
41767      * @param {Field} etc (optional)
41768      * @return Column The column container object
41769      */
41770     column : function(c){
41771         var col = new Roo.form.Column(c);
41772         this.start(col);
41773         if(arguments.length > 1){ // duplicate code required because of Opera
41774             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41775             this.end();
41776         }
41777         return col;
41778     },
41779
41780     /**
41781      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41782      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41783      * until end() is called.
41784      * @param {Object} config The config to pass to the fieldset
41785      * @param {Field} field1 (optional)
41786      * @param {Field} field2 (optional)
41787      * @param {Field} etc (optional)
41788      * @return FieldSet The fieldset container object
41789      */
41790     fieldset : function(c){
41791         var fs = new Roo.form.FieldSet(c);
41792         this.start(fs);
41793         if(arguments.length > 1){ // duplicate code required because of Opera
41794             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41795             this.end();
41796         }
41797         return fs;
41798     },
41799
41800     /**
41801      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41802      * fields are added and the container is closed. If no fields are passed the container remains open
41803      * until end() is called.
41804      * @param {Object} config The config to pass to the Layout
41805      * @param {Field} field1 (optional)
41806      * @param {Field} field2 (optional)
41807      * @param {Field} etc (optional)
41808      * @return Layout The container object
41809      */
41810     container : function(c){
41811         var l = new Roo.form.Layout(c);
41812         this.start(l);
41813         if(arguments.length > 1){ // duplicate code required because of Opera
41814             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41815             this.end();
41816         }
41817         return l;
41818     },
41819
41820     /**
41821      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41822      * @param {Object} container A Roo.form.Layout or subclass of Layout
41823      * @return {Form} this
41824      */
41825     start : function(c){
41826         // cascade label info
41827         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41828         this.active.stack.push(c);
41829         c.ownerCt = this.active;
41830         this.active = c;
41831         return this;
41832     },
41833
41834     /**
41835      * Closes the current open container
41836      * @return {Form} this
41837      */
41838     end : function(){
41839         if(this.active == this.root){
41840             return this;
41841         }
41842         this.active = this.active.ownerCt;
41843         return this;
41844     },
41845
41846     /**
41847      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41848      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41849      * as the label of the field.
41850      * @param {Field} field1
41851      * @param {Field} field2 (optional)
41852      * @param {Field} etc. (optional)
41853      * @return {Form} this
41854      */
41855     add : function(){
41856         this.active.stack.push.apply(this.active.stack, arguments);
41857         this.allItems.push.apply(this.allItems,arguments);
41858         var r = [];
41859         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41860             if(a[i].isFormField){
41861                 r.push(a[i]);
41862             }
41863         }
41864         if(r.length > 0){
41865             Roo.form.Form.superclass.add.apply(this, r);
41866         }
41867         return this;
41868     },
41869     
41870
41871     
41872     
41873     
41874      /**
41875      * Find any element that has been added to a form, using it's ID or name
41876      * This can include framesets, columns etc. along with regular fields..
41877      * @param {String} id - id or name to find.
41878      
41879      * @return {Element} e - or false if nothing found.
41880      */
41881     findbyId : function(id)
41882     {
41883         var ret = false;
41884         if (!id) {
41885             return ret;
41886         }
41887         Roo.each(this.allItems, function(f){
41888             if (f.id == id || f.name == id ){
41889                 ret = f;
41890                 return false;
41891             }
41892         });
41893         return ret;
41894     },
41895
41896     
41897     
41898     /**
41899      * Render this form into the passed container. This should only be called once!
41900      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41901      * @return {Form} this
41902      */
41903     render : function(ct)
41904     {
41905         
41906         
41907         
41908         ct = Roo.get(ct);
41909         var o = this.autoCreate || {
41910             tag: 'form',
41911             method : this.method || 'POST',
41912             id : this.id || Roo.id()
41913         };
41914         this.initEl(ct.createChild(o));
41915
41916         this.root.render(this.el);
41917         
41918        
41919              
41920         this.items.each(function(f){
41921             f.render('x-form-el-'+f.id);
41922         });
41923
41924         if(this.buttons.length > 0){
41925             // tables are required to maintain order and for correct IE layout
41926             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41927                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41928                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41929             }}, null, true);
41930             var tr = tb.getElementsByTagName('tr')[0];
41931             for(var i = 0, len = this.buttons.length; i < len; i++) {
41932                 var b = this.buttons[i];
41933                 var td = document.createElement('td');
41934                 td.className = 'x-form-btn-td';
41935                 b.render(tr.appendChild(td));
41936             }
41937         }
41938         if(this.monitorValid){ // initialize after render
41939             this.startMonitoring();
41940         }
41941         this.fireEvent('rendered', this);
41942         return this;
41943     },
41944
41945     /**
41946      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41947      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41948      * object or a valid Roo.DomHelper element config
41949      * @param {Function} handler The function called when the button is clicked
41950      * @param {Object} scope (optional) The scope of the handler function
41951      * @return {Roo.Button}
41952      */
41953     addButton : function(config, handler, scope){
41954         var bc = {
41955             handler: handler,
41956             scope: scope,
41957             minWidth: this.minButtonWidth,
41958             hideParent:true
41959         };
41960         if(typeof config == "string"){
41961             bc.text = config;
41962         }else{
41963             Roo.apply(bc, config);
41964         }
41965         var btn = new Roo.Button(null, bc);
41966         this.buttons.push(btn);
41967         return btn;
41968     },
41969
41970      /**
41971      * Adds a series of form elements (using the xtype property as the factory method.
41972      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41973      * @param {Object} config 
41974      */
41975     
41976     addxtype : function()
41977     {
41978         var ar = Array.prototype.slice.call(arguments, 0);
41979         var ret = false;
41980         for(var i = 0; i < ar.length; i++) {
41981             if (!ar[i]) {
41982                 continue; // skip -- if this happends something invalid got sent, we 
41983                 // should ignore it, as basically that interface element will not show up
41984                 // and that should be pretty obvious!!
41985             }
41986             
41987             if (Roo.form[ar[i].xtype]) {
41988                 ar[i].form = this;
41989                 var fe = Roo.factory(ar[i], Roo.form);
41990                 if (!ret) {
41991                     ret = fe;
41992                 }
41993                 fe.form = this;
41994                 if (fe.store) {
41995                     fe.store.form = this;
41996                 }
41997                 if (fe.isLayout) {  
41998                          
41999                     this.start(fe);
42000                     this.allItems.push(fe);
42001                     if (fe.items && fe.addxtype) {
42002                         fe.addxtype.apply(fe, fe.items);
42003                         delete fe.items;
42004                     }
42005                      this.end();
42006                     continue;
42007                 }
42008                 
42009                 
42010                  
42011                 this.add(fe);
42012               //  console.log('adding ' + ar[i].xtype);
42013             }
42014             if (ar[i].xtype == 'Button') {  
42015                 //console.log('adding button');
42016                 //console.log(ar[i]);
42017                 this.addButton(ar[i]);
42018                 this.allItems.push(fe);
42019                 continue;
42020             }
42021             
42022             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42023                 alert('end is not supported on xtype any more, use items');
42024             //    this.end();
42025             //    //console.log('adding end');
42026             }
42027             
42028         }
42029         return ret;
42030     },
42031     
42032     /**
42033      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42034      * option "monitorValid"
42035      */
42036     startMonitoring : function(){
42037         if(!this.bound){
42038             this.bound = true;
42039             Roo.TaskMgr.start({
42040                 run : this.bindHandler,
42041                 interval : this.monitorPoll || 200,
42042                 scope: this
42043             });
42044         }
42045     },
42046
42047     /**
42048      * Stops monitoring of the valid state of this form
42049      */
42050     stopMonitoring : function(){
42051         this.bound = false;
42052     },
42053
42054     // private
42055     bindHandler : function(){
42056         if(!this.bound){
42057             return false; // stops binding
42058         }
42059         var valid = true;
42060         this.items.each(function(f){
42061             if(!f.isValid(true)){
42062                 valid = false;
42063                 return false;
42064             }
42065         });
42066         for(var i = 0, len = this.buttons.length; i < len; i++){
42067             var btn = this.buttons[i];
42068             if(btn.formBind === true && btn.disabled === valid){
42069                 btn.setDisabled(!valid);
42070             }
42071         }
42072         this.fireEvent('clientvalidation', this, valid);
42073     }
42074     
42075     
42076     
42077     
42078     
42079     
42080     
42081     
42082 });
42083
42084
42085 // back compat
42086 Roo.Form = Roo.form.Form;
42087 /*
42088  * Based on:
42089  * Ext JS Library 1.1.1
42090  * Copyright(c) 2006-2007, Ext JS, LLC.
42091  *
42092  * Originally Released Under LGPL - original licence link has changed is not relivant.
42093  *
42094  * Fork - LGPL
42095  * <script type="text/javascript">
42096  */
42097  
42098  /**
42099  * @class Roo.form.Action
42100  * Internal Class used to handle form actions
42101  * @constructor
42102  * @param {Roo.form.BasicForm} el The form element or its id
42103  * @param {Object} config Configuration options
42104  */
42105  
42106  
42107 // define the action interface
42108 Roo.form.Action = function(form, options){
42109     this.form = form;
42110     this.options = options || {};
42111 };
42112 /**
42113  * Client Validation Failed
42114  * @const 
42115  */
42116 Roo.form.Action.CLIENT_INVALID = 'client';
42117 /**
42118  * Server Validation Failed
42119  * @const 
42120  */
42121  Roo.form.Action.SERVER_INVALID = 'server';
42122  /**
42123  * Connect to Server Failed
42124  * @const 
42125  */
42126 Roo.form.Action.CONNECT_FAILURE = 'connect';
42127 /**
42128  * Reading Data from Server Failed
42129  * @const 
42130  */
42131 Roo.form.Action.LOAD_FAILURE = 'load';
42132
42133 Roo.form.Action.prototype = {
42134     type : 'default',
42135     failureType : undefined,
42136     response : undefined,
42137     result : undefined,
42138
42139     // interface method
42140     run : function(options){
42141
42142     },
42143
42144     // interface method
42145     success : function(response){
42146
42147     },
42148
42149     // interface method
42150     handleResponse : function(response){
42151
42152     },
42153
42154     // default connection failure
42155     failure : function(response){
42156         
42157         this.response = response;
42158         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42159         this.form.afterAction(this, false);
42160     },
42161
42162     processResponse : function(response){
42163         this.response = response;
42164         if(!response.responseText){
42165             return true;
42166         }
42167         this.result = this.handleResponse(response);
42168         return this.result;
42169     },
42170
42171     // utility functions used internally
42172     getUrl : function(appendParams){
42173         var url = this.options.url || this.form.url || this.form.el.dom.action;
42174         if(appendParams){
42175             var p = this.getParams();
42176             if(p){
42177                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42178             }
42179         }
42180         return url;
42181     },
42182
42183     getMethod : function(){
42184         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42185     },
42186
42187     getParams : function(){
42188         var bp = this.form.baseParams;
42189         var p = this.options.params;
42190         if(p){
42191             if(typeof p == "object"){
42192                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42193             }else if(typeof p == 'string' && bp){
42194                 p += '&' + Roo.urlEncode(bp);
42195             }
42196         }else if(bp){
42197             p = Roo.urlEncode(bp);
42198         }
42199         return p;
42200     },
42201
42202     createCallback : function(){
42203         return {
42204             success: this.success,
42205             failure: this.failure,
42206             scope: this,
42207             timeout: (this.form.timeout*1000),
42208             upload: this.form.fileUpload ? this.success : undefined
42209         };
42210     }
42211 };
42212
42213 Roo.form.Action.Submit = function(form, options){
42214     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42215 };
42216
42217 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42218     type : 'submit',
42219
42220     haveProgress : false,
42221     uploadComplete : false,
42222     
42223     // uploadProgress indicator.
42224     uploadProgress : function()
42225     {
42226         if (!this.form.progressUrl) {
42227             return;
42228         }
42229         
42230         if (!this.haveProgress) {
42231             Roo.MessageBox.progress("Uploading", "Uploading");
42232         }
42233         if (this.uploadComplete) {
42234            Roo.MessageBox.hide();
42235            return;
42236         }
42237         
42238         this.haveProgress = true;
42239    
42240         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42241         
42242         var c = new Roo.data.Connection();
42243         c.request({
42244             url : this.form.progressUrl,
42245             params: {
42246                 id : uid
42247             },
42248             method: 'GET',
42249             success : function(req){
42250                //console.log(data);
42251                 var rdata = false;
42252                 var edata;
42253                 try  {
42254                    rdata = Roo.decode(req.responseText)
42255                 } catch (e) {
42256                     Roo.log("Invalid data from server..");
42257                     Roo.log(edata);
42258                     return;
42259                 }
42260                 if (!rdata || !rdata.success) {
42261                     Roo.log(rdata);
42262                     return;
42263                 }
42264                 var data = rdata.data;
42265                 
42266                 if (this.uploadComplete) {
42267                    Roo.MessageBox.hide();
42268                    return;
42269                 }
42270                    
42271                 if (data){
42272                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42273                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42274                     );
42275                 }
42276                 this.uploadProgress.defer(2000,this);
42277             },
42278        
42279             failure: function(data) {
42280                 Roo.log('progress url failed ');
42281                 Roo.log(data);
42282             },
42283             scope : this
42284         });
42285            
42286     },
42287     
42288     
42289     run : function()
42290     {
42291         // run get Values on the form, so it syncs any secondary forms.
42292         this.form.getValues();
42293         
42294         var o = this.options;
42295         var method = this.getMethod();
42296         var isPost = method == 'POST';
42297         if(o.clientValidation === false || this.form.isValid()){
42298             
42299             if (this.form.progressUrl) {
42300                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42301                     (new Date() * 1) + '' + Math.random());
42302                     
42303             } 
42304             
42305             
42306             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42307                 form:this.form.el.dom,
42308                 url:this.getUrl(!isPost),
42309                 method: method,
42310                 params:isPost ? this.getParams() : null,
42311                 isUpload: this.form.fileUpload
42312             }));
42313             
42314             this.uploadProgress();
42315
42316         }else if (o.clientValidation !== false){ // client validation failed
42317             this.failureType = Roo.form.Action.CLIENT_INVALID;
42318             this.form.afterAction(this, false);
42319         }
42320     },
42321
42322     success : function(response)
42323     {
42324         this.uploadComplete= true;
42325         if (this.haveProgress) {
42326             Roo.MessageBox.hide();
42327         }
42328         
42329         
42330         var result = this.processResponse(response);
42331         if(result === true || result.success){
42332             this.form.afterAction(this, true);
42333             return;
42334         }
42335         if(result.errors){
42336             this.form.markInvalid(result.errors);
42337             this.failureType = Roo.form.Action.SERVER_INVALID;
42338         }
42339         this.form.afterAction(this, false);
42340     },
42341     failure : function(response)
42342     {
42343         this.uploadComplete= true;
42344         if (this.haveProgress) {
42345             Roo.MessageBox.hide();
42346         }
42347         
42348         this.response = response;
42349         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42350         this.form.afterAction(this, false);
42351     },
42352     
42353     handleResponse : function(response){
42354         if(this.form.errorReader){
42355             var rs = this.form.errorReader.read(response);
42356             var errors = [];
42357             if(rs.records){
42358                 for(var i = 0, len = rs.records.length; i < len; i++) {
42359                     var r = rs.records[i];
42360                     errors[i] = r.data;
42361                 }
42362             }
42363             if(errors.length < 1){
42364                 errors = null;
42365             }
42366             return {
42367                 success : rs.success,
42368                 errors : errors
42369             };
42370         }
42371         var ret = false;
42372         try {
42373             ret = Roo.decode(response.responseText);
42374         } catch (e) {
42375             ret = {
42376                 success: false,
42377                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42378                 errors : []
42379             };
42380         }
42381         return ret;
42382         
42383     }
42384 });
42385
42386
42387 Roo.form.Action.Load = function(form, options){
42388     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42389     this.reader = this.form.reader;
42390 };
42391
42392 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42393     type : 'load',
42394
42395     run : function(){
42396         
42397         Roo.Ajax.request(Roo.apply(
42398                 this.createCallback(), {
42399                     method:this.getMethod(),
42400                     url:this.getUrl(false),
42401                     params:this.getParams()
42402         }));
42403     },
42404
42405     success : function(response){
42406         
42407         var result = this.processResponse(response);
42408         if(result === true || !result.success || !result.data){
42409             this.failureType = Roo.form.Action.LOAD_FAILURE;
42410             this.form.afterAction(this, false);
42411             return;
42412         }
42413         this.form.clearInvalid();
42414         this.form.setValues(result.data);
42415         this.form.afterAction(this, true);
42416     },
42417
42418     handleResponse : function(response){
42419         if(this.form.reader){
42420             var rs = this.form.reader.read(response);
42421             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42422             return {
42423                 success : rs.success,
42424                 data : data
42425             };
42426         }
42427         return Roo.decode(response.responseText);
42428     }
42429 });
42430
42431 Roo.form.Action.ACTION_TYPES = {
42432     'load' : Roo.form.Action.Load,
42433     'submit' : Roo.form.Action.Submit
42434 };/*
42435  * Based on:
42436  * Ext JS Library 1.1.1
42437  * Copyright(c) 2006-2007, Ext JS, LLC.
42438  *
42439  * Originally Released Under LGPL - original licence link has changed is not relivant.
42440  *
42441  * Fork - LGPL
42442  * <script type="text/javascript">
42443  */
42444  
42445 /**
42446  * @class Roo.form.Layout
42447  * @extends Roo.Component
42448  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42449  * @constructor
42450  * @param {Object} config Configuration options
42451  */
42452 Roo.form.Layout = function(config){
42453     var xitems = [];
42454     if (config.items) {
42455         xitems = config.items;
42456         delete config.items;
42457     }
42458     Roo.form.Layout.superclass.constructor.call(this, config);
42459     this.stack = [];
42460     Roo.each(xitems, this.addxtype, this);
42461      
42462 };
42463
42464 Roo.extend(Roo.form.Layout, Roo.Component, {
42465     /**
42466      * @cfg {String/Object} autoCreate
42467      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42468      */
42469     /**
42470      * @cfg {String/Object/Function} style
42471      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42472      * a function which returns such a specification.
42473      */
42474     /**
42475      * @cfg {String} labelAlign
42476      * Valid values are "left," "top" and "right" (defaults to "left")
42477      */
42478     /**
42479      * @cfg {Number} labelWidth
42480      * Fixed width in pixels of all field labels (defaults to undefined)
42481      */
42482     /**
42483      * @cfg {Boolean} clear
42484      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42485      */
42486     clear : true,
42487     /**
42488      * @cfg {String} labelSeparator
42489      * The separator to use after field labels (defaults to ':')
42490      */
42491     labelSeparator : ':',
42492     /**
42493      * @cfg {Boolean} hideLabels
42494      * True to suppress the display of field labels in this layout (defaults to false)
42495      */
42496     hideLabels : false,
42497
42498     // private
42499     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42500     
42501     isLayout : true,
42502     
42503     // private
42504     onRender : function(ct, position){
42505         if(this.el){ // from markup
42506             this.el = Roo.get(this.el);
42507         }else {  // generate
42508             var cfg = this.getAutoCreate();
42509             this.el = ct.createChild(cfg, position);
42510         }
42511         if(this.style){
42512             this.el.applyStyles(this.style);
42513         }
42514         if(this.labelAlign){
42515             this.el.addClass('x-form-label-'+this.labelAlign);
42516         }
42517         if(this.hideLabels){
42518             this.labelStyle = "display:none";
42519             this.elementStyle = "padding-left:0;";
42520         }else{
42521             if(typeof this.labelWidth == 'number'){
42522                 this.labelStyle = "width:"+this.labelWidth+"px;";
42523                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42524             }
42525             if(this.labelAlign == 'top'){
42526                 this.labelStyle = "width:auto;";
42527                 this.elementStyle = "padding-left:0;";
42528             }
42529         }
42530         var stack = this.stack;
42531         var slen = stack.length;
42532         if(slen > 0){
42533             if(!this.fieldTpl){
42534                 var t = new Roo.Template(
42535                     '<div class="x-form-item {5}">',
42536                         '<label for="{0}" style="{2}">{1}{4}</label>',
42537                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42538                         '</div>',
42539                     '</div><div class="x-form-clear-left"></div>'
42540                 );
42541                 t.disableFormats = true;
42542                 t.compile();
42543                 Roo.form.Layout.prototype.fieldTpl = t;
42544             }
42545             for(var i = 0; i < slen; i++) {
42546                 if(stack[i].isFormField){
42547                     this.renderField(stack[i]);
42548                 }else{
42549                     this.renderComponent(stack[i]);
42550                 }
42551             }
42552         }
42553         if(this.clear){
42554             this.el.createChild({cls:'x-form-clear'});
42555         }
42556     },
42557
42558     // private
42559     renderField : function(f){
42560         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42561                f.id, //0
42562                f.fieldLabel, //1
42563                f.labelStyle||this.labelStyle||'', //2
42564                this.elementStyle||'', //3
42565                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42566                f.itemCls||this.itemCls||''  //5
42567        ], true).getPrevSibling());
42568     },
42569
42570     // private
42571     renderComponent : function(c){
42572         c.render(c.isLayout ? this.el : this.el.createChild());    
42573     },
42574     /**
42575      * Adds a object form elements (using the xtype property as the factory method.)
42576      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42577      * @param {Object} config 
42578      */
42579     addxtype : function(o)
42580     {
42581         // create the lement.
42582         o.form = this.form;
42583         var fe = Roo.factory(o, Roo.form);
42584         this.form.allItems.push(fe);
42585         this.stack.push(fe);
42586         
42587         if (fe.isFormField) {
42588             this.form.items.add(fe);
42589         }
42590          
42591         return fe;
42592     }
42593 });
42594
42595 /**
42596  * @class Roo.form.Column
42597  * @extends Roo.form.Layout
42598  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42599  * @constructor
42600  * @param {Object} config Configuration options
42601  */
42602 Roo.form.Column = function(config){
42603     Roo.form.Column.superclass.constructor.call(this, config);
42604 };
42605
42606 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42607     /**
42608      * @cfg {Number/String} width
42609      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42610      */
42611     /**
42612      * @cfg {String/Object} autoCreate
42613      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42614      */
42615
42616     // private
42617     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42618
42619     // private
42620     onRender : function(ct, position){
42621         Roo.form.Column.superclass.onRender.call(this, ct, position);
42622         if(this.width){
42623             this.el.setWidth(this.width);
42624         }
42625     }
42626 });
42627
42628
42629 /**
42630  * @class Roo.form.Row
42631  * @extends Roo.form.Layout
42632  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42633  * @constructor
42634  * @param {Object} config Configuration options
42635  */
42636
42637  
42638 Roo.form.Row = function(config){
42639     Roo.form.Row.superclass.constructor.call(this, config);
42640 };
42641  
42642 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42643       /**
42644      * @cfg {Number/String} width
42645      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42646      */
42647     /**
42648      * @cfg {Number/String} height
42649      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42650      */
42651     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42652     
42653     padWidth : 20,
42654     // private
42655     onRender : function(ct, position){
42656         //console.log('row render');
42657         if(!this.rowTpl){
42658             var t = new Roo.Template(
42659                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42660                     '<label for="{0}" style="{2}">{1}{4}</label>',
42661                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42662                     '</div>',
42663                 '</div>'
42664             );
42665             t.disableFormats = true;
42666             t.compile();
42667             Roo.form.Layout.prototype.rowTpl = t;
42668         }
42669         this.fieldTpl = this.rowTpl;
42670         
42671         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42672         var labelWidth = 100;
42673         
42674         if ((this.labelAlign != 'top')) {
42675             if (typeof this.labelWidth == 'number') {
42676                 labelWidth = this.labelWidth
42677             }
42678             this.padWidth =  20 + labelWidth;
42679             
42680         }
42681         
42682         Roo.form.Column.superclass.onRender.call(this, ct, position);
42683         if(this.width){
42684             this.el.setWidth(this.width);
42685         }
42686         if(this.height){
42687             this.el.setHeight(this.height);
42688         }
42689     },
42690     
42691     // private
42692     renderField : function(f){
42693         f.fieldEl = this.fieldTpl.append(this.el, [
42694                f.id, f.fieldLabel,
42695                f.labelStyle||this.labelStyle||'',
42696                this.elementStyle||'',
42697                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42698                f.itemCls||this.itemCls||'',
42699                f.width ? f.width + this.padWidth : 160 + this.padWidth
42700        ],true);
42701     }
42702 });
42703  
42704
42705 /**
42706  * @class Roo.form.FieldSet
42707  * @extends Roo.form.Layout
42708  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42709  * @constructor
42710  * @param {Object} config Configuration options
42711  */
42712 Roo.form.FieldSet = function(config){
42713     Roo.form.FieldSet.superclass.constructor.call(this, config);
42714 };
42715
42716 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42717     /**
42718      * @cfg {String} legend
42719      * The text to display as the legend for the FieldSet (defaults to '')
42720      */
42721     /**
42722      * @cfg {String/Object} autoCreate
42723      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42724      */
42725
42726     // private
42727     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42728
42729     // private
42730     onRender : function(ct, position){
42731         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42732         if(this.legend){
42733             this.setLegend(this.legend);
42734         }
42735     },
42736
42737     // private
42738     setLegend : function(text){
42739         if(this.rendered){
42740             this.el.child('legend').update(text);
42741         }
42742     }
42743 });/*
42744  * Based on:
42745  * Ext JS Library 1.1.1
42746  * Copyright(c) 2006-2007, Ext JS, LLC.
42747  *
42748  * Originally Released Under LGPL - original licence link has changed is not relivant.
42749  *
42750  * Fork - LGPL
42751  * <script type="text/javascript">
42752  */
42753 /**
42754  * @class Roo.form.VTypes
42755  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42756  * @singleton
42757  */
42758 Roo.form.VTypes = function(){
42759     // closure these in so they are only created once.
42760     var alpha = /^[a-zA-Z_]+$/;
42761     var alphanum = /^[a-zA-Z0-9_]+$/;
42762     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42763     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42764
42765     // All these messages and functions are configurable
42766     return {
42767         /**
42768          * The function used to validate email addresses
42769          * @param {String} value The email address
42770          */
42771         'email' : function(v){
42772             return email.test(v);
42773         },
42774         /**
42775          * The error text to display when the email validation function returns false
42776          * @type String
42777          */
42778         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42779         /**
42780          * The keystroke filter mask to be applied on email input
42781          * @type RegExp
42782          */
42783         'emailMask' : /[a-z0-9_\.\-@]/i,
42784
42785         /**
42786          * The function used to validate URLs
42787          * @param {String} value The URL
42788          */
42789         'url' : function(v){
42790             return url.test(v);
42791         },
42792         /**
42793          * The error text to display when the url validation function returns false
42794          * @type String
42795          */
42796         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42797         
42798         /**
42799          * The function used to validate alpha values
42800          * @param {String} value The value
42801          */
42802         'alpha' : function(v){
42803             return alpha.test(v);
42804         },
42805         /**
42806          * The error text to display when the alpha validation function returns false
42807          * @type String
42808          */
42809         'alphaText' : 'This field should only contain letters and _',
42810         /**
42811          * The keystroke filter mask to be applied on alpha input
42812          * @type RegExp
42813          */
42814         'alphaMask' : /[a-z_]/i,
42815
42816         /**
42817          * The function used to validate alphanumeric values
42818          * @param {String} value The value
42819          */
42820         'alphanum' : function(v){
42821             return alphanum.test(v);
42822         },
42823         /**
42824          * The error text to display when the alphanumeric validation function returns false
42825          * @type String
42826          */
42827         'alphanumText' : 'This field should only contain letters, numbers and _',
42828         /**
42829          * The keystroke filter mask to be applied on alphanumeric input
42830          * @type RegExp
42831          */
42832         'alphanumMask' : /[a-z0-9_]/i
42833     };
42834 }();//<script type="text/javascript">
42835
42836 /**
42837  * @class Roo.form.FCKeditor
42838  * @extends Roo.form.TextArea
42839  * Wrapper around the FCKEditor http://www.fckeditor.net
42840  * @constructor
42841  * Creates a new FCKeditor
42842  * @param {Object} config Configuration options
42843  */
42844 Roo.form.FCKeditor = function(config){
42845     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42846     this.addEvents({
42847          /**
42848          * @event editorinit
42849          * Fired when the editor is initialized - you can add extra handlers here..
42850          * @param {FCKeditor} this
42851          * @param {Object} the FCK object.
42852          */
42853         editorinit : true
42854     });
42855     
42856     
42857 };
42858 Roo.form.FCKeditor.editors = { };
42859 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42860 {
42861     //defaultAutoCreate : {
42862     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42863     //},
42864     // private
42865     /**
42866      * @cfg {Object} fck options - see fck manual for details.
42867      */
42868     fckconfig : false,
42869     
42870     /**
42871      * @cfg {Object} fck toolbar set (Basic or Default)
42872      */
42873     toolbarSet : 'Basic',
42874     /**
42875      * @cfg {Object} fck BasePath
42876      */ 
42877     basePath : '/fckeditor/',
42878     
42879     
42880     frame : false,
42881     
42882     value : '',
42883     
42884    
42885     onRender : function(ct, position)
42886     {
42887         if(!this.el){
42888             this.defaultAutoCreate = {
42889                 tag: "textarea",
42890                 style:"width:300px;height:60px;",
42891                 autocomplete: "off"
42892             };
42893         }
42894         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42895         /*
42896         if(this.grow){
42897             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42898             if(this.preventScrollbars){
42899                 this.el.setStyle("overflow", "hidden");
42900             }
42901             this.el.setHeight(this.growMin);
42902         }
42903         */
42904         //console.log('onrender' + this.getId() );
42905         Roo.form.FCKeditor.editors[this.getId()] = this;
42906          
42907
42908         this.replaceTextarea() ;
42909         
42910     },
42911     
42912     getEditor : function() {
42913         return this.fckEditor;
42914     },
42915     /**
42916      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42917      * @param {Mixed} value The value to set
42918      */
42919     
42920     
42921     setValue : function(value)
42922     {
42923         //console.log('setValue: ' + value);
42924         
42925         if(typeof(value) == 'undefined') { // not sure why this is happending...
42926             return;
42927         }
42928         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42929         
42930         //if(!this.el || !this.getEditor()) {
42931         //    this.value = value;
42932             //this.setValue.defer(100,this,[value]);    
42933         //    return;
42934         //} 
42935         
42936         if(!this.getEditor()) {
42937             return;
42938         }
42939         
42940         this.getEditor().SetData(value);
42941         
42942         //
42943
42944     },
42945
42946     /**
42947      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42948      * @return {Mixed} value The field value
42949      */
42950     getValue : function()
42951     {
42952         
42953         if (this.frame && this.frame.dom.style.display == 'none') {
42954             return Roo.form.FCKeditor.superclass.getValue.call(this);
42955         }
42956         
42957         if(!this.el || !this.getEditor()) {
42958            
42959            // this.getValue.defer(100,this); 
42960             return this.value;
42961         }
42962        
42963         
42964         var value=this.getEditor().GetData();
42965         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42966         return Roo.form.FCKeditor.superclass.getValue.call(this);
42967         
42968
42969     },
42970
42971     /**
42972      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42973      * @return {Mixed} value The field value
42974      */
42975     getRawValue : function()
42976     {
42977         if (this.frame && this.frame.dom.style.display == 'none') {
42978             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42979         }
42980         
42981         if(!this.el || !this.getEditor()) {
42982             //this.getRawValue.defer(100,this); 
42983             return this.value;
42984             return;
42985         }
42986         
42987         
42988         
42989         var value=this.getEditor().GetData();
42990         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42991         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42992          
42993     },
42994     
42995     setSize : function(w,h) {
42996         
42997         
42998         
42999         //if (this.frame && this.frame.dom.style.display == 'none') {
43000         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43001         //    return;
43002         //}
43003         //if(!this.el || !this.getEditor()) {
43004         //    this.setSize.defer(100,this, [w,h]); 
43005         //    return;
43006         //}
43007         
43008         
43009         
43010         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43011         
43012         this.frame.dom.setAttribute('width', w);
43013         this.frame.dom.setAttribute('height', h);
43014         this.frame.setSize(w,h);
43015         
43016     },
43017     
43018     toggleSourceEdit : function(value) {
43019         
43020       
43021          
43022         this.el.dom.style.display = value ? '' : 'none';
43023         this.frame.dom.style.display = value ?  'none' : '';
43024         
43025     },
43026     
43027     
43028     focus: function(tag)
43029     {
43030         if (this.frame.dom.style.display == 'none') {
43031             return Roo.form.FCKeditor.superclass.focus.call(this);
43032         }
43033         if(!this.el || !this.getEditor()) {
43034             this.focus.defer(100,this, [tag]); 
43035             return;
43036         }
43037         
43038         
43039         
43040         
43041         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43042         this.getEditor().Focus();
43043         if (tgs.length) {
43044             if (!this.getEditor().Selection.GetSelection()) {
43045                 this.focus.defer(100,this, [tag]); 
43046                 return;
43047             }
43048             
43049             
43050             var r = this.getEditor().EditorDocument.createRange();
43051             r.setStart(tgs[0],0);
43052             r.setEnd(tgs[0],0);
43053             this.getEditor().Selection.GetSelection().removeAllRanges();
43054             this.getEditor().Selection.GetSelection().addRange(r);
43055             this.getEditor().Focus();
43056         }
43057         
43058     },
43059     
43060     
43061     
43062     replaceTextarea : function()
43063     {
43064         if ( document.getElementById( this.getId() + '___Frame' ) )
43065             return ;
43066         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43067         //{
43068             // We must check the elements firstly using the Id and then the name.
43069         var oTextarea = document.getElementById( this.getId() );
43070         
43071         var colElementsByName = document.getElementsByName( this.getId() ) ;
43072          
43073         oTextarea.style.display = 'none' ;
43074
43075         if ( oTextarea.tabIndex ) {            
43076             this.TabIndex = oTextarea.tabIndex ;
43077         }
43078         
43079         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43080         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43081         this.frame = Roo.get(this.getId() + '___Frame')
43082     },
43083     
43084     _getConfigHtml : function()
43085     {
43086         var sConfig = '' ;
43087
43088         for ( var o in this.fckconfig ) {
43089             sConfig += sConfig.length > 0  ? '&amp;' : '';
43090             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43091         }
43092
43093         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43094     },
43095     
43096     
43097     _getIFrameHtml : function()
43098     {
43099         var sFile = 'fckeditor.html' ;
43100         /* no idea what this is about..
43101         try
43102         {
43103             if ( (/fcksource=true/i).test( window.top.location.search ) )
43104                 sFile = 'fckeditor.original.html' ;
43105         }
43106         catch (e) { 
43107         */
43108
43109         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43110         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43111         
43112         
43113         var html = '<iframe id="' + this.getId() +
43114             '___Frame" src="' + sLink +
43115             '" width="' + this.width +
43116             '" height="' + this.height + '"' +
43117             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43118             ' frameborder="0" scrolling="no"></iframe>' ;
43119
43120         return html ;
43121     },
43122     
43123     _insertHtmlBefore : function( html, element )
43124     {
43125         if ( element.insertAdjacentHTML )       {
43126             // IE
43127             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43128         } else { // Gecko
43129             var oRange = document.createRange() ;
43130             oRange.setStartBefore( element ) ;
43131             var oFragment = oRange.createContextualFragment( html );
43132             element.parentNode.insertBefore( oFragment, element ) ;
43133         }
43134     }
43135     
43136     
43137   
43138     
43139     
43140     
43141     
43142
43143 });
43144
43145 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43146
43147 function FCKeditor_OnComplete(editorInstance){
43148     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43149     f.fckEditor = editorInstance;
43150     //console.log("loaded");
43151     f.fireEvent('editorinit', f, editorInstance);
43152
43153   
43154
43155  
43156
43157
43158
43159
43160
43161
43162
43163
43164
43165
43166
43167
43168
43169
43170
43171 //<script type="text/javascript">
43172 /**
43173  * @class Roo.form.GridField
43174  * @extends Roo.form.Field
43175  * Embed a grid (or editable grid into a form)
43176  * STATUS ALPHA
43177  * 
43178  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43179  * it needs 
43180  * xgrid.store = Roo.data.Store
43181  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43182  * xgrid.store.reader = Roo.data.JsonReader 
43183  * 
43184  * 
43185  * @constructor
43186  * Creates a new GridField
43187  * @param {Object} config Configuration options
43188  */
43189 Roo.form.GridField = function(config){
43190     Roo.form.GridField.superclass.constructor.call(this, config);
43191      
43192 };
43193
43194 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43195     /**
43196      * @cfg {Number} width  - used to restrict width of grid..
43197      */
43198     width : 100,
43199     /**
43200      * @cfg {Number} height - used to restrict height of grid..
43201      */
43202     height : 50,
43203      /**
43204      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43205          * 
43206          *}
43207      */
43208     xgrid : false, 
43209     /**
43210      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43211      * {tag: "input", type: "checkbox", autocomplete: "off"})
43212      */
43213    // defaultAutoCreate : { tag: 'div' },
43214     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43215     /**
43216      * @cfg {String} addTitle Text to include for adding a title.
43217      */
43218     addTitle : false,
43219     //
43220     onResize : function(){
43221         Roo.form.Field.superclass.onResize.apply(this, arguments);
43222     },
43223
43224     initEvents : function(){
43225         // Roo.form.Checkbox.superclass.initEvents.call(this);
43226         // has no events...
43227        
43228     },
43229
43230
43231     getResizeEl : function(){
43232         return this.wrap;
43233     },
43234
43235     getPositionEl : function(){
43236         return this.wrap;
43237     },
43238
43239     // private
43240     onRender : function(ct, position){
43241         
43242         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43243         var style = this.style;
43244         delete this.style;
43245         
43246         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43247         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43248         this.viewEl = this.wrap.createChild({ tag: 'div' });
43249         if (style) {
43250             this.viewEl.applyStyles(style);
43251         }
43252         if (this.width) {
43253             this.viewEl.setWidth(this.width);
43254         }
43255         if (this.height) {
43256             this.viewEl.setHeight(this.height);
43257         }
43258         //if(this.inputValue !== undefined){
43259         //this.setValue(this.value);
43260         
43261         
43262         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43263         
43264         
43265         this.grid.render();
43266         this.grid.getDataSource().on('remove', this.refreshValue, this);
43267         this.grid.getDataSource().on('update', this.refreshValue, this);
43268         this.grid.on('afteredit', this.refreshValue, this);
43269  
43270     },
43271      
43272     
43273     /**
43274      * Sets the value of the item. 
43275      * @param {String} either an object  or a string..
43276      */
43277     setValue : function(v){
43278         //this.value = v;
43279         v = v || []; // empty set..
43280         // this does not seem smart - it really only affects memoryproxy grids..
43281         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43282             var ds = this.grid.getDataSource();
43283             // assumes a json reader..
43284             var data = {}
43285             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43286             ds.loadData( data);
43287         }
43288         // clear selection so it does not get stale.
43289         if (this.grid.sm) { 
43290             this.grid.sm.clearSelections();
43291         }
43292         
43293         Roo.form.GridField.superclass.setValue.call(this, v);
43294         this.refreshValue();
43295         // should load data in the grid really....
43296     },
43297     
43298     // private
43299     refreshValue: function() {
43300          var val = [];
43301         this.grid.getDataSource().each(function(r) {
43302             val.push(r.data);
43303         });
43304         this.el.dom.value = Roo.encode(val);
43305     }
43306     
43307      
43308     
43309     
43310 });/*
43311  * Based on:
43312  * Ext JS Library 1.1.1
43313  * Copyright(c) 2006-2007, Ext JS, LLC.
43314  *
43315  * Originally Released Under LGPL - original licence link has changed is not relivant.
43316  *
43317  * Fork - LGPL
43318  * <script type="text/javascript">
43319  */
43320 /**
43321  * @class Roo.form.DisplayField
43322  * @extends Roo.form.Field
43323  * A generic Field to display non-editable data.
43324  * @constructor
43325  * Creates a new Display Field item.
43326  * @param {Object} config Configuration options
43327  */
43328 Roo.form.DisplayField = function(config){
43329     Roo.form.DisplayField.superclass.constructor.call(this, config);
43330     
43331 };
43332
43333 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43334     inputType:      'hidden',
43335     allowBlank:     true,
43336     readOnly:         true,
43337     
43338  
43339     /**
43340      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43341      */
43342     focusClass : undefined,
43343     /**
43344      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43345      */
43346     fieldClass: 'x-form-field',
43347     
43348      /**
43349      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43350      */
43351     valueRenderer: undefined,
43352     
43353     width: 100,
43354     /**
43355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43356      * {tag: "input", type: "checkbox", autocomplete: "off"})
43357      */
43358      
43359  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43360
43361     onResize : function(){
43362         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43363         
43364     },
43365
43366     initEvents : function(){
43367         // Roo.form.Checkbox.superclass.initEvents.call(this);
43368         // has no events...
43369        
43370     },
43371
43372
43373     getResizeEl : function(){
43374         return this.wrap;
43375     },
43376
43377     getPositionEl : function(){
43378         return this.wrap;
43379     },
43380
43381     // private
43382     onRender : function(ct, position){
43383         
43384         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43385         //if(this.inputValue !== undefined){
43386         this.wrap = this.el.wrap();
43387         
43388         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43389         
43390         if (this.bodyStyle) {
43391             this.viewEl.applyStyles(this.bodyStyle);
43392         }
43393         //this.viewEl.setStyle('padding', '2px');
43394         
43395         this.setValue(this.value);
43396         
43397     },
43398 /*
43399     // private
43400     initValue : Roo.emptyFn,
43401
43402   */
43403
43404         // private
43405     onClick : function(){
43406         
43407     },
43408
43409     /**
43410      * Sets the checked state of the checkbox.
43411      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43412      */
43413     setValue : function(v){
43414         this.value = v;
43415         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43416         // this might be called before we have a dom element..
43417         if (!this.viewEl) {
43418             return;
43419         }
43420         this.viewEl.dom.innerHTML = html;
43421         Roo.form.DisplayField.superclass.setValue.call(this, v);
43422
43423     }
43424 });/*
43425  * 
43426  * Licence- LGPL
43427  * 
43428  */
43429
43430 /**
43431  * @class Roo.form.DayPicker
43432  * @extends Roo.form.Field
43433  * A Day picker show [M] [T] [W] ....
43434  * @constructor
43435  * Creates a new Day Picker
43436  * @param {Object} config Configuration options
43437  */
43438 Roo.form.DayPicker= function(config){
43439     Roo.form.DayPicker.superclass.constructor.call(this, config);
43440      
43441 };
43442
43443 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43444     /**
43445      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43446      */
43447     focusClass : undefined,
43448     /**
43449      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43450      */
43451     fieldClass: "x-form-field",
43452    
43453     /**
43454      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43455      * {tag: "input", type: "checkbox", autocomplete: "off"})
43456      */
43457     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43458     
43459    
43460     actionMode : 'viewEl', 
43461     //
43462     // private
43463  
43464     inputType : 'hidden',
43465     
43466      
43467     inputElement: false, // real input element?
43468     basedOn: false, // ????
43469     
43470     isFormField: true, // not sure where this is needed!!!!
43471
43472     onResize : function(){
43473         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43474         if(!this.boxLabel){
43475             this.el.alignTo(this.wrap, 'c-c');
43476         }
43477     },
43478
43479     initEvents : function(){
43480         Roo.form.Checkbox.superclass.initEvents.call(this);
43481         this.el.on("click", this.onClick,  this);
43482         this.el.on("change", this.onClick,  this);
43483     },
43484
43485
43486     getResizeEl : function(){
43487         return this.wrap;
43488     },
43489
43490     getPositionEl : function(){
43491         return this.wrap;
43492     },
43493
43494     
43495     // private
43496     onRender : function(ct, position){
43497         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43498        
43499         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43500         
43501         var r1 = '<table><tr>';
43502         var r2 = '<tr class="x-form-daypick-icons">';
43503         for (var i=0; i < 7; i++) {
43504             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43505             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43506         }
43507         
43508         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43509         viewEl.select('img').on('click', this.onClick, this);
43510         this.viewEl = viewEl;   
43511         
43512         
43513         // this will not work on Chrome!!!
43514         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43515         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43516         
43517         
43518           
43519
43520     },
43521
43522     // private
43523     initValue : Roo.emptyFn,
43524
43525     /**
43526      * Returns the checked state of the checkbox.
43527      * @return {Boolean} True if checked, else false
43528      */
43529     getValue : function(){
43530         return this.el.dom.value;
43531         
43532     },
43533
43534         // private
43535     onClick : function(e){ 
43536         //this.setChecked(!this.checked);
43537         Roo.get(e.target).toggleClass('x-menu-item-checked');
43538         this.refreshValue();
43539         //if(this.el.dom.checked != this.checked){
43540         //    this.setValue(this.el.dom.checked);
43541        // }
43542     },
43543     
43544     // private
43545     refreshValue : function()
43546     {
43547         var val = '';
43548         this.viewEl.select('img',true).each(function(e,i,n)  {
43549             val += e.is(".x-menu-item-checked") ? String(n) : '';
43550         });
43551         this.setValue(val, true);
43552     },
43553
43554     /**
43555      * Sets the checked state of the checkbox.
43556      * On is always based on a string comparison between inputValue and the param.
43557      * @param {Boolean/String} value - the value to set 
43558      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43559      */
43560     setValue : function(v,suppressEvent){
43561         if (!this.el.dom) {
43562             return;
43563         }
43564         var old = this.el.dom.value ;
43565         this.el.dom.value = v;
43566         if (suppressEvent) {
43567             return ;
43568         }
43569          
43570         // update display..
43571         this.viewEl.select('img',true).each(function(e,i,n)  {
43572             
43573             var on = e.is(".x-menu-item-checked");
43574             var newv = v.indexOf(String(n)) > -1;
43575             if (on != newv) {
43576                 e.toggleClass('x-menu-item-checked');
43577             }
43578             
43579         });
43580         
43581         
43582         this.fireEvent('change', this, v, old);
43583         
43584         
43585     },
43586    
43587     // handle setting of hidden value by some other method!!?!?
43588     setFromHidden: function()
43589     {
43590         if(!this.el){
43591             return;
43592         }
43593         //console.log("SET FROM HIDDEN");
43594         //alert('setFrom hidden');
43595         this.setValue(this.el.dom.value);
43596     },
43597     
43598     onDestroy : function()
43599     {
43600         if(this.viewEl){
43601             Roo.get(this.viewEl).remove();
43602         }
43603          
43604         Roo.form.DayPicker.superclass.onDestroy.call(this);
43605     }
43606
43607 });/*
43608  * RooJS Library 1.1.1
43609  * Copyright(c) 2008-2011  Alan Knowles
43610  *
43611  * License - LGPL
43612  */
43613  
43614
43615 /**
43616  * @class Roo.form.ComboCheck
43617  * @extends Roo.form.ComboBox
43618  * A combobox for multiple select items.
43619  *
43620  * FIXME - could do with a reset button..
43621  * 
43622  * @constructor
43623  * Create a new ComboCheck
43624  * @param {Object} config Configuration options
43625  */
43626 Roo.form.ComboCheck = function(config){
43627     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43628     // should verify some data...
43629     // like
43630     // hiddenName = required..
43631     // displayField = required
43632     // valudField == required
43633     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43634     var _t = this;
43635     Roo.each(req, function(e) {
43636         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43637             throw "Roo.form.ComboCheck : missing value for: " + e;
43638         }
43639     });
43640     
43641     
43642 };
43643
43644 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43645      
43646      
43647     editable : false,
43648      
43649     selectedClass: 'x-menu-item-checked', 
43650     
43651     // private
43652     onRender : function(ct, position){
43653         var _t = this;
43654         
43655         
43656         
43657         if(!this.tpl){
43658             var cls = 'x-combo-list';
43659
43660             
43661             this.tpl =  new Roo.Template({
43662                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43663                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43664                    '<span>{' + this.displayField + '}</span>' +
43665                     '</div>' 
43666                 
43667             });
43668         }
43669  
43670         
43671         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43672         this.view.singleSelect = false;
43673         this.view.multiSelect = true;
43674         this.view.toggleSelect = true;
43675         this.pageTb.add(new Roo.Toolbar.Fill(), {
43676             
43677             text: 'Done',
43678             handler: function()
43679             {
43680                 _t.collapse();
43681             }
43682         });
43683     },
43684     
43685     onViewOver : function(e, t){
43686         // do nothing...
43687         return;
43688         
43689     },
43690     
43691     onViewClick : function(doFocus,index){
43692         return;
43693         
43694     },
43695     select: function () {
43696         //Roo.log("SELECT CALLED");
43697     },
43698      
43699     selectByValue : function(xv, scrollIntoView){
43700         var ar = this.getValueArray();
43701         var sels = [];
43702         
43703         Roo.each(ar, function(v) {
43704             if(v === undefined || v === null){
43705                 return;
43706             }
43707             var r = this.findRecord(this.valueField, v);
43708             if(r){
43709                 sels.push(this.store.indexOf(r))
43710                 
43711             }
43712         },this);
43713         this.view.select(sels);
43714         return false;
43715     },
43716     
43717     
43718     
43719     onSelect : function(record, index){
43720        // Roo.log("onselect Called");
43721        // this is only called by the clear button now..
43722         this.view.clearSelections();
43723         this.setValue('[]');
43724         if (this.value != this.valueBefore) {
43725             this.fireEvent('change', this, this.value, this.valueBefore);
43726         }
43727     },
43728     getValueArray : function()
43729     {
43730         var ar = [] ;
43731         
43732         try {
43733             //Roo.log(this.value);
43734             if (typeof(this.value) == 'undefined') {
43735                 return [];
43736             }
43737             var ar = Roo.decode(this.value);
43738             return  ar instanceof Array ? ar : []; //?? valid?
43739             
43740         } catch(e) {
43741             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43742             return [];
43743         }
43744          
43745     },
43746     expand : function ()
43747     {
43748         Roo.form.ComboCheck.superclass.expand.call(this);
43749         this.valueBefore = this.value;
43750         
43751
43752     },
43753     
43754     collapse : function(){
43755         Roo.form.ComboCheck.superclass.collapse.call(this);
43756         var sl = this.view.getSelectedIndexes();
43757         var st = this.store;
43758         var nv = [];
43759         var tv = [];
43760         var r;
43761         Roo.each(sl, function(i) {
43762             r = st.getAt(i);
43763             nv.push(r.get(this.valueField));
43764         },this);
43765         this.setValue(Roo.encode(nv));
43766         if (this.value != this.valueBefore) {
43767
43768             this.fireEvent('change', this, this.value, this.valueBefore);
43769         }
43770         
43771     },
43772     
43773     setValue : function(v){
43774         // Roo.log(v);
43775         this.value = v;
43776         
43777         var vals = this.getValueArray();
43778         var tv = [];
43779         Roo.each(vals, function(k) {
43780             var r = this.findRecord(this.valueField, k);
43781             if(r){
43782                 tv.push(r.data[this.displayField]);
43783             }else if(this.valueNotFoundText !== undefined){
43784                 tv.push( this.valueNotFoundText );
43785             }
43786         },this);
43787        // Roo.log(tv);
43788         
43789         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43790         this.hiddenField.value = v;
43791         this.value = v;
43792     }
43793     
43794 });//<script type="text/javasscript">
43795  
43796
43797 /**
43798  * @class Roo.DDView
43799  * A DnD enabled version of Roo.View.
43800  * @param {Element/String} container The Element in which to create the View.
43801  * @param {String} tpl The template string used to create the markup for each element of the View
43802  * @param {Object} config The configuration properties. These include all the config options of
43803  * {@link Roo.View} plus some specific to this class.<br>
43804  * <p>
43805  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43806  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43807  * <p>
43808  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43809 .x-view-drag-insert-above {
43810         border-top:1px dotted #3366cc;
43811 }
43812 .x-view-drag-insert-below {
43813         border-bottom:1px dotted #3366cc;
43814 }
43815 </code></pre>
43816  * 
43817  */
43818  
43819 Roo.DDView = function(container, tpl, config) {
43820     Roo.DDView.superclass.constructor.apply(this, arguments);
43821     this.getEl().setStyle("outline", "0px none");
43822     this.getEl().unselectable();
43823     if (this.dragGroup) {
43824                 this.setDraggable(this.dragGroup.split(","));
43825     }
43826     if (this.dropGroup) {
43827                 this.setDroppable(this.dropGroup.split(","));
43828     }
43829     if (this.deletable) {
43830         this.setDeletable();
43831     }
43832     this.isDirtyFlag = false;
43833         this.addEvents({
43834                 "drop" : true
43835         });
43836 };
43837
43838 Roo.extend(Roo.DDView, Roo.View, {
43839 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43840 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43841 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43842 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43843
43844         isFormField: true,
43845
43846         reset: Roo.emptyFn,
43847         
43848         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43849
43850         validate: function() {
43851                 return true;
43852         },
43853         
43854         destroy: function() {
43855                 this.purgeListeners();
43856                 this.getEl.removeAllListeners();
43857                 this.getEl().remove();
43858                 if (this.dragZone) {
43859                         if (this.dragZone.destroy) {
43860                                 this.dragZone.destroy();
43861                         }
43862                 }
43863                 if (this.dropZone) {
43864                         if (this.dropZone.destroy) {
43865                                 this.dropZone.destroy();
43866                         }
43867                 }
43868         },
43869
43870 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43871         getName: function() {
43872                 return this.name;
43873         },
43874
43875 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43876         setValue: function(v) {
43877                 if (!this.store) {
43878                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43879                 }
43880                 var data = {};
43881                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43882                 this.store.proxy = new Roo.data.MemoryProxy(data);
43883                 this.store.load();
43884         },
43885
43886 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43887         getValue: function() {
43888                 var result = '(';
43889                 this.store.each(function(rec) {
43890                         result += rec.id + ',';
43891                 });
43892                 return result.substr(0, result.length - 1) + ')';
43893         },
43894         
43895         getIds: function() {
43896                 var i = 0, result = new Array(this.store.getCount());
43897                 this.store.each(function(rec) {
43898                         result[i++] = rec.id;
43899                 });
43900                 return result;
43901         },
43902         
43903         isDirty: function() {
43904                 return this.isDirtyFlag;
43905         },
43906
43907 /**
43908  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43909  *      whole Element becomes the target, and this causes the drop gesture to append.
43910  */
43911     getTargetFromEvent : function(e) {
43912                 var target = e.getTarget();
43913                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43914                 target = target.parentNode;
43915                 }
43916                 if (!target) {
43917                         target = this.el.dom.lastChild || this.el.dom;
43918                 }
43919                 return target;
43920     },
43921
43922 /**
43923  *      Create the drag data which consists of an object which has the property "ddel" as
43924  *      the drag proxy element. 
43925  */
43926     getDragData : function(e) {
43927         var target = this.findItemFromChild(e.getTarget());
43928                 if(target) {
43929                         this.handleSelection(e);
43930                         var selNodes = this.getSelectedNodes();
43931             var dragData = {
43932                 source: this,
43933                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43934                 nodes: selNodes,
43935                 records: []
43936                         };
43937                         var selectedIndices = this.getSelectedIndexes();
43938                         for (var i = 0; i < selectedIndices.length; i++) {
43939                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43940                         }
43941                         if (selNodes.length == 1) {
43942                                 dragData.ddel = target.cloneNode(true); // the div element
43943                         } else {
43944                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43945                                 div.className = 'multi-proxy';
43946                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43947                                         div.appendChild(selNodes[i].cloneNode(true));
43948                                 }
43949                                 dragData.ddel = div;
43950                         }
43951             //console.log(dragData)
43952             //console.log(dragData.ddel.innerHTML)
43953                         return dragData;
43954                 }
43955         //console.log('nodragData')
43956                 return false;
43957     },
43958     
43959 /**     Specify to which ddGroup items in this DDView may be dragged. */
43960     setDraggable: function(ddGroup) {
43961         if (ddGroup instanceof Array) {
43962                 Roo.each(ddGroup, this.setDraggable, this);
43963                 return;
43964         }
43965         if (this.dragZone) {
43966                 this.dragZone.addToGroup(ddGroup);
43967         } else {
43968                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43969                                 containerScroll: true,
43970                                 ddGroup: ddGroup 
43971
43972                         });
43973 //                      Draggability implies selection. DragZone's mousedown selects the element.
43974                         if (!this.multiSelect) { this.singleSelect = true; }
43975
43976 //                      Wire the DragZone's handlers up to methods in *this*
43977                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43978                 }
43979     },
43980
43981 /**     Specify from which ddGroup this DDView accepts drops. */
43982     setDroppable: function(ddGroup) {
43983         if (ddGroup instanceof Array) {
43984                 Roo.each(ddGroup, this.setDroppable, this);
43985                 return;
43986         }
43987         if (this.dropZone) {
43988                 this.dropZone.addToGroup(ddGroup);
43989         } else {
43990                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43991                                 containerScroll: true,
43992                                 ddGroup: ddGroup
43993                         });
43994
43995 //                      Wire the DropZone's handlers up to methods in *this*
43996                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43997                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43998                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43999                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44000                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44001                 }
44002     },
44003
44004 /**     Decide whether to drop above or below a View node. */
44005     getDropPoint : function(e, n, dd){
44006         if (n == this.el.dom) { return "above"; }
44007                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44008                 var c = t + (b - t) / 2;
44009                 var y = Roo.lib.Event.getPageY(e);
44010                 if(y <= c) {
44011                         return "above";
44012                 }else{
44013                         return "below";
44014                 }
44015     },
44016
44017     onNodeEnter : function(n, dd, e, data){
44018                 return false;
44019     },
44020     
44021     onNodeOver : function(n, dd, e, data){
44022                 var pt = this.getDropPoint(e, n, dd);
44023                 // set the insert point style on the target node
44024                 var dragElClass = this.dropNotAllowed;
44025                 if (pt) {
44026                         var targetElClass;
44027                         if (pt == "above"){
44028                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44029                                 targetElClass = "x-view-drag-insert-above";
44030                         } else {
44031                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44032                                 targetElClass = "x-view-drag-insert-below";
44033                         }
44034                         if (this.lastInsertClass != targetElClass){
44035                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44036                                 this.lastInsertClass = targetElClass;
44037                         }
44038                 }
44039                 return dragElClass;
44040         },
44041
44042     onNodeOut : function(n, dd, e, data){
44043                 this.removeDropIndicators(n);
44044     },
44045
44046     onNodeDrop : function(n, dd, e, data){
44047         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44048                 return false;
44049         }
44050         var pt = this.getDropPoint(e, n, dd);
44051                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44052                 if (pt == "below") { insertAt++; }
44053                 for (var i = 0; i < data.records.length; i++) {
44054                         var r = data.records[i];
44055                         var dup = this.store.getById(r.id);
44056                         if (dup && (dd != this.dragZone)) {
44057                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44058                         } else {
44059                                 if (data.copy) {
44060                                         this.store.insert(insertAt++, r.copy());
44061                                 } else {
44062                                         data.source.isDirtyFlag = true;
44063                                         r.store.remove(r);
44064                                         this.store.insert(insertAt++, r);
44065                                 }
44066                                 this.isDirtyFlag = true;
44067                         }
44068                 }
44069                 this.dragZone.cachedTarget = null;
44070                 return true;
44071     },
44072
44073     removeDropIndicators : function(n){
44074                 if(n){
44075                         Roo.fly(n).removeClass([
44076                                 "x-view-drag-insert-above",
44077                                 "x-view-drag-insert-below"]);
44078                         this.lastInsertClass = "_noclass";
44079                 }
44080     },
44081
44082 /**
44083  *      Utility method. Add a delete option to the DDView's context menu.
44084  *      @param {String} imageUrl The URL of the "delete" icon image.
44085  */
44086         setDeletable: function(imageUrl) {
44087                 if (!this.singleSelect && !this.multiSelect) {
44088                         this.singleSelect = true;
44089                 }
44090                 var c = this.getContextMenu();
44091                 this.contextMenu.on("itemclick", function(item) {
44092                         switch (item.id) {
44093                                 case "delete":
44094                                         this.remove(this.getSelectedIndexes());
44095                                         break;
44096                         }
44097                 }, this);
44098                 this.contextMenu.add({
44099                         icon: imageUrl,
44100                         id: "delete",
44101                         text: 'Delete'
44102                 });
44103         },
44104         
44105 /**     Return the context menu for this DDView. */
44106         getContextMenu: function() {
44107                 if (!this.contextMenu) {
44108 //                      Create the View's context menu
44109                         this.contextMenu = new Roo.menu.Menu({
44110                                 id: this.id + "-contextmenu"
44111                         });
44112                         this.el.on("contextmenu", this.showContextMenu, this);
44113                 }
44114                 return this.contextMenu;
44115         },
44116         
44117         disableContextMenu: function() {
44118                 if (this.contextMenu) {
44119                         this.el.un("contextmenu", this.showContextMenu, this);
44120                 }
44121         },
44122
44123         showContextMenu: function(e, item) {
44124         item = this.findItemFromChild(e.getTarget());
44125                 if (item) {
44126                         e.stopEvent();
44127                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44128                         this.contextMenu.showAt(e.getXY());
44129             }
44130     },
44131
44132 /**
44133  *      Remove {@link Roo.data.Record}s at the specified indices.
44134  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44135  */
44136     remove: function(selectedIndices) {
44137                 selectedIndices = [].concat(selectedIndices);
44138                 for (var i = 0; i < selectedIndices.length; i++) {
44139                         var rec = this.store.getAt(selectedIndices[i]);
44140                         this.store.remove(rec);
44141                 }
44142     },
44143
44144 /**
44145  *      Double click fires the event, but also, if this is draggable, and there is only one other
44146  *      related DropZone, it transfers the selected node.
44147  */
44148     onDblClick : function(e){
44149         var item = this.findItemFromChild(e.getTarget());
44150         if(item){
44151             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44152                 return false;
44153             }
44154             if (this.dragGroup) {
44155                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44156                     while (targets.indexOf(this.dropZone) > -1) {
44157                             targets.remove(this.dropZone);
44158                                 }
44159                     if (targets.length == 1) {
44160                                         this.dragZone.cachedTarget = null;
44161                         var el = Roo.get(targets[0].getEl());
44162                         var box = el.getBox(true);
44163                         targets[0].onNodeDrop(el.dom, {
44164                                 target: el.dom,
44165                                 xy: [box.x, box.y + box.height - 1]
44166                         }, null, this.getDragData(e));
44167                     }
44168                 }
44169         }
44170     },
44171     
44172     handleSelection: function(e) {
44173                 this.dragZone.cachedTarget = null;
44174         var item = this.findItemFromChild(e.getTarget());
44175         if (!item) {
44176                 this.clearSelections(true);
44177                 return;
44178         }
44179                 if (item && (this.multiSelect || this.singleSelect)){
44180                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44181                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44182                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44183                                 this.unselect(item);
44184                         } else {
44185                                 this.select(item, this.multiSelect && e.ctrlKey);
44186                                 this.lastSelection = item;
44187                         }
44188                 }
44189     },
44190
44191     onItemClick : function(item, index, e){
44192                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44193                         return false;
44194                 }
44195                 return true;
44196     },
44197
44198     unselect : function(nodeInfo, suppressEvent){
44199                 var node = this.getNode(nodeInfo);
44200                 if(node && this.isSelected(node)){
44201                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44202                                 Roo.fly(node).removeClass(this.selectedClass);
44203                                 this.selections.remove(node);
44204                                 if(!suppressEvent){
44205                                         this.fireEvent("selectionchange", this, this.selections);
44206                                 }
44207                         }
44208                 }
44209     }
44210 });
44211 /*
44212  * Based on:
44213  * Ext JS Library 1.1.1
44214  * Copyright(c) 2006-2007, Ext JS, LLC.
44215  *
44216  * Originally Released Under LGPL - original licence link has changed is not relivant.
44217  *
44218  * Fork - LGPL
44219  * <script type="text/javascript">
44220  */
44221  
44222 /**
44223  * @class Roo.LayoutManager
44224  * @extends Roo.util.Observable
44225  * Base class for layout managers.
44226  */
44227 Roo.LayoutManager = function(container, config){
44228     Roo.LayoutManager.superclass.constructor.call(this);
44229     this.el = Roo.get(container);
44230     // ie scrollbar fix
44231     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44232         document.body.scroll = "no";
44233     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44234         this.el.position('relative');
44235     }
44236     this.id = this.el.id;
44237     this.el.addClass("x-layout-container");
44238     /** false to disable window resize monitoring @type Boolean */
44239     this.monitorWindowResize = true;
44240     this.regions = {};
44241     this.addEvents({
44242         /**
44243          * @event layout
44244          * Fires when a layout is performed. 
44245          * @param {Roo.LayoutManager} this
44246          */
44247         "layout" : true,
44248         /**
44249          * @event regionresized
44250          * Fires when the user resizes a region. 
44251          * @param {Roo.LayoutRegion} region The resized region
44252          * @param {Number} newSize The new size (width for east/west, height for north/south)
44253          */
44254         "regionresized" : true,
44255         /**
44256          * @event regioncollapsed
44257          * Fires when a region is collapsed. 
44258          * @param {Roo.LayoutRegion} region The collapsed region
44259          */
44260         "regioncollapsed" : true,
44261         /**
44262          * @event regionexpanded
44263          * Fires when a region is expanded.  
44264          * @param {Roo.LayoutRegion} region The expanded region
44265          */
44266         "regionexpanded" : true
44267     });
44268     this.updating = false;
44269     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44270 };
44271
44272 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44273     /**
44274      * Returns true if this layout is currently being updated
44275      * @return {Boolean}
44276      */
44277     isUpdating : function(){
44278         return this.updating; 
44279     },
44280     
44281     /**
44282      * Suspend the LayoutManager from doing auto-layouts while
44283      * making multiple add or remove calls
44284      */
44285     beginUpdate : function(){
44286         this.updating = true;    
44287     },
44288     
44289     /**
44290      * Restore auto-layouts and optionally disable the manager from performing a layout
44291      * @param {Boolean} noLayout true to disable a layout update 
44292      */
44293     endUpdate : function(noLayout){
44294         this.updating = false;
44295         if(!noLayout){
44296             this.layout();
44297         }    
44298     },
44299     
44300     layout: function(){
44301         
44302     },
44303     
44304     onRegionResized : function(region, newSize){
44305         this.fireEvent("regionresized", region, newSize);
44306         this.layout();
44307     },
44308     
44309     onRegionCollapsed : function(region){
44310         this.fireEvent("regioncollapsed", region);
44311     },
44312     
44313     onRegionExpanded : function(region){
44314         this.fireEvent("regionexpanded", region);
44315     },
44316         
44317     /**
44318      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44319      * performs box-model adjustments.
44320      * @return {Object} The size as an object {width: (the width), height: (the height)}
44321      */
44322     getViewSize : function(){
44323         var size;
44324         if(this.el.dom != document.body){
44325             size = this.el.getSize();
44326         }else{
44327             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44328         }
44329         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44330         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44331         return size;
44332     },
44333     
44334     /**
44335      * Returns the Element this layout is bound to.
44336      * @return {Roo.Element}
44337      */
44338     getEl : function(){
44339         return this.el;
44340     },
44341     
44342     /**
44343      * Returns the specified region.
44344      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44345      * @return {Roo.LayoutRegion}
44346      */
44347     getRegion : function(target){
44348         return this.regions[target.toLowerCase()];
44349     },
44350     
44351     onWindowResize : function(){
44352         if(this.monitorWindowResize){
44353             this.layout();
44354         }
44355     }
44356 });/*
44357  * Based on:
44358  * Ext JS Library 1.1.1
44359  * Copyright(c) 2006-2007, Ext JS, LLC.
44360  *
44361  * Originally Released Under LGPL - original licence link has changed is not relivant.
44362  *
44363  * Fork - LGPL
44364  * <script type="text/javascript">
44365  */
44366 /**
44367  * @class Roo.BorderLayout
44368  * @extends Roo.LayoutManager
44369  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44370  * please see: <br><br>
44371  * <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>
44372  * <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>
44373  * Example:
44374  <pre><code>
44375  var layout = new Roo.BorderLayout(document.body, {
44376     north: {
44377         initialSize: 25,
44378         titlebar: false
44379     },
44380     west: {
44381         split:true,
44382         initialSize: 200,
44383         minSize: 175,
44384         maxSize: 400,
44385         titlebar: true,
44386         collapsible: true
44387     },
44388     east: {
44389         split:true,
44390         initialSize: 202,
44391         minSize: 175,
44392         maxSize: 400,
44393         titlebar: true,
44394         collapsible: true
44395     },
44396     south: {
44397         split:true,
44398         initialSize: 100,
44399         minSize: 100,
44400         maxSize: 200,
44401         titlebar: true,
44402         collapsible: true
44403     },
44404     center: {
44405         titlebar: true,
44406         autoScroll:true,
44407         resizeTabs: true,
44408         minTabWidth: 50,
44409         preferredTabWidth: 150
44410     }
44411 });
44412
44413 // shorthand
44414 var CP = Roo.ContentPanel;
44415
44416 layout.beginUpdate();
44417 layout.add("north", new CP("north", "North"));
44418 layout.add("south", new CP("south", {title: "South", closable: true}));
44419 layout.add("west", new CP("west", {title: "West"}));
44420 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44421 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44422 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44423 layout.getRegion("center").showPanel("center1");
44424 layout.endUpdate();
44425 </code></pre>
44426
44427 <b>The container the layout is rendered into can be either the body element or any other element.
44428 If it is not the body element, the container needs to either be an absolute positioned element,
44429 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44430 the container size if it is not the body element.</b>
44431
44432 * @constructor
44433 * Create a new BorderLayout
44434 * @param {String/HTMLElement/Element} container The container this layout is bound to
44435 * @param {Object} config Configuration options
44436  */
44437 Roo.BorderLayout = function(container, config){
44438     config = config || {};
44439     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44440     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44441     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44442         var target = this.factory.validRegions[i];
44443         if(config[target]){
44444             this.addRegion(target, config[target]);
44445         }
44446     }
44447 };
44448
44449 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44450     /**
44451      * Creates and adds a new region if it doesn't already exist.
44452      * @param {String} target The target region key (north, south, east, west or center).
44453      * @param {Object} config The regions config object
44454      * @return {BorderLayoutRegion} The new region
44455      */
44456     addRegion : function(target, config){
44457         if(!this.regions[target]){
44458             var r = this.factory.create(target, this, config);
44459             this.bindRegion(target, r);
44460         }
44461         return this.regions[target];
44462     },
44463
44464     // private (kinda)
44465     bindRegion : function(name, r){
44466         this.regions[name] = r;
44467         r.on("visibilitychange", this.layout, this);
44468         r.on("paneladded", this.layout, this);
44469         r.on("panelremoved", this.layout, this);
44470         r.on("invalidated", this.layout, this);
44471         r.on("resized", this.onRegionResized, this);
44472         r.on("collapsed", this.onRegionCollapsed, this);
44473         r.on("expanded", this.onRegionExpanded, this);
44474     },
44475
44476     /**
44477      * Performs a layout update.
44478      */
44479     layout : function(){
44480         if(this.updating) return;
44481         var size = this.getViewSize();
44482         var w = size.width;
44483         var h = size.height;
44484         var centerW = w;
44485         var centerH = h;
44486         var centerY = 0;
44487         var centerX = 0;
44488         //var x = 0, y = 0;
44489
44490         var rs = this.regions;
44491         var north = rs["north"];
44492         var south = rs["south"]; 
44493         var west = rs["west"];
44494         var east = rs["east"];
44495         var center = rs["center"];
44496         //if(this.hideOnLayout){ // not supported anymore
44497             //c.el.setStyle("display", "none");
44498         //}
44499         if(north && north.isVisible()){
44500             var b = north.getBox();
44501             var m = north.getMargins();
44502             b.width = w - (m.left+m.right);
44503             b.x = m.left;
44504             b.y = m.top;
44505             centerY = b.height + b.y + m.bottom;
44506             centerH -= centerY;
44507             north.updateBox(this.safeBox(b));
44508         }
44509         if(south && south.isVisible()){
44510             var b = south.getBox();
44511             var m = south.getMargins();
44512             b.width = w - (m.left+m.right);
44513             b.x = m.left;
44514             var totalHeight = (b.height + m.top + m.bottom);
44515             b.y = h - totalHeight + m.top;
44516             centerH -= totalHeight;
44517             south.updateBox(this.safeBox(b));
44518         }
44519         if(west && west.isVisible()){
44520             var b = west.getBox();
44521             var m = west.getMargins();
44522             b.height = centerH - (m.top+m.bottom);
44523             b.x = m.left;
44524             b.y = centerY + m.top;
44525             var totalWidth = (b.width + m.left + m.right);
44526             centerX += totalWidth;
44527             centerW -= totalWidth;
44528             west.updateBox(this.safeBox(b));
44529         }
44530         if(east && east.isVisible()){
44531             var b = east.getBox();
44532             var m = east.getMargins();
44533             b.height = centerH - (m.top+m.bottom);
44534             var totalWidth = (b.width + m.left + m.right);
44535             b.x = w - totalWidth + m.left;
44536             b.y = centerY + m.top;
44537             centerW -= totalWidth;
44538             east.updateBox(this.safeBox(b));
44539         }
44540         if(center){
44541             var m = center.getMargins();
44542             var centerBox = {
44543                 x: centerX + m.left,
44544                 y: centerY + m.top,
44545                 width: centerW - (m.left+m.right),
44546                 height: centerH - (m.top+m.bottom)
44547             };
44548             //if(this.hideOnLayout){
44549                 //center.el.setStyle("display", "block");
44550             //}
44551             center.updateBox(this.safeBox(centerBox));
44552         }
44553         this.el.repaint();
44554         this.fireEvent("layout", this);
44555     },
44556
44557     // private
44558     safeBox : function(box){
44559         box.width = Math.max(0, box.width);
44560         box.height = Math.max(0, box.height);
44561         return box;
44562     },
44563
44564     /**
44565      * Adds a ContentPanel (or subclass) to this layout.
44566      * @param {String} target The target region key (north, south, east, west or center).
44567      * @param {Roo.ContentPanel} panel The panel to add
44568      * @return {Roo.ContentPanel} The added panel
44569      */
44570     add : function(target, panel){
44571          
44572         target = target.toLowerCase();
44573         return this.regions[target].add(panel);
44574     },
44575
44576     /**
44577      * Remove a ContentPanel (or subclass) to this layout.
44578      * @param {String} target The target region key (north, south, east, west or center).
44579      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44580      * @return {Roo.ContentPanel} The removed panel
44581      */
44582     remove : function(target, panel){
44583         target = target.toLowerCase();
44584         return this.regions[target].remove(panel);
44585     },
44586
44587     /**
44588      * Searches all regions for a panel with the specified id
44589      * @param {String} panelId
44590      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44591      */
44592     findPanel : function(panelId){
44593         var rs = this.regions;
44594         for(var target in rs){
44595             if(typeof rs[target] != "function"){
44596                 var p = rs[target].getPanel(panelId);
44597                 if(p){
44598                     return p;
44599                 }
44600             }
44601         }
44602         return null;
44603     },
44604
44605     /**
44606      * Searches all regions for a panel with the specified id and activates (shows) it.
44607      * @param {String/ContentPanel} panelId The panels id or the panel itself
44608      * @return {Roo.ContentPanel} The shown panel or null
44609      */
44610     showPanel : function(panelId) {
44611       var rs = this.regions;
44612       for(var target in rs){
44613          var r = rs[target];
44614          if(typeof r != "function"){
44615             if(r.hasPanel(panelId)){
44616                return r.showPanel(panelId);
44617             }
44618          }
44619       }
44620       return null;
44621    },
44622
44623    /**
44624      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44625      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44626      */
44627     restoreState : function(provider){
44628         if(!provider){
44629             provider = Roo.state.Manager;
44630         }
44631         var sm = new Roo.LayoutStateManager();
44632         sm.init(this, provider);
44633     },
44634
44635     /**
44636      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44637      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44638      * a valid ContentPanel config object.  Example:
44639      * <pre><code>
44640 // Create the main layout
44641 var layout = new Roo.BorderLayout('main-ct', {
44642     west: {
44643         split:true,
44644         minSize: 175,
44645         titlebar: true
44646     },
44647     center: {
44648         title:'Components'
44649     }
44650 }, 'main-ct');
44651
44652 // Create and add multiple ContentPanels at once via configs
44653 layout.batchAdd({
44654    west: {
44655        id: 'source-files',
44656        autoCreate:true,
44657        title:'Ext Source Files',
44658        autoScroll:true,
44659        fitToFrame:true
44660    },
44661    center : {
44662        el: cview,
44663        autoScroll:true,
44664        fitToFrame:true,
44665        toolbar: tb,
44666        resizeEl:'cbody'
44667    }
44668 });
44669 </code></pre>
44670      * @param {Object} regions An object containing ContentPanel configs by region name
44671      */
44672     batchAdd : function(regions){
44673         this.beginUpdate();
44674         for(var rname in regions){
44675             var lr = this.regions[rname];
44676             if(lr){
44677                 this.addTypedPanels(lr, regions[rname]);
44678             }
44679         }
44680         this.endUpdate();
44681     },
44682
44683     // private
44684     addTypedPanels : function(lr, ps){
44685         if(typeof ps == 'string'){
44686             lr.add(new Roo.ContentPanel(ps));
44687         }
44688         else if(ps instanceof Array){
44689             for(var i =0, len = ps.length; i < len; i++){
44690                 this.addTypedPanels(lr, ps[i]);
44691             }
44692         }
44693         else if(!ps.events){ // raw config?
44694             var el = ps.el;
44695             delete ps.el; // prevent conflict
44696             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44697         }
44698         else {  // panel object assumed!
44699             lr.add(ps);
44700         }
44701     },
44702     /**
44703      * Adds a xtype elements to the layout.
44704      * <pre><code>
44705
44706 layout.addxtype({
44707        xtype : 'ContentPanel',
44708        region: 'west',
44709        items: [ .... ]
44710    }
44711 );
44712
44713 layout.addxtype({
44714         xtype : 'NestedLayoutPanel',
44715         region: 'west',
44716         layout: {
44717            center: { },
44718            west: { }   
44719         },
44720         items : [ ... list of content panels or nested layout panels.. ]
44721    }
44722 );
44723 </code></pre>
44724      * @param {Object} cfg Xtype definition of item to add.
44725      */
44726     addxtype : function(cfg)
44727     {
44728         // basically accepts a pannel...
44729         // can accept a layout region..!?!?
44730         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44731         
44732         if (!cfg.xtype.match(/Panel$/)) {
44733             return false;
44734         }
44735         var ret = false;
44736         
44737         if (typeof(cfg.region) == 'undefined') {
44738             Roo.log("Failed to add Panel, region was not set");
44739             Roo.log(cfg);
44740             return false;
44741         }
44742         var region = cfg.region;
44743         delete cfg.region;
44744         
44745           
44746         var xitems = [];
44747         if (cfg.items) {
44748             xitems = cfg.items;
44749             delete cfg.items;
44750         }
44751         var nb = false;
44752         
44753         switch(cfg.xtype) 
44754         {
44755             case 'ContentPanel':  // ContentPanel (el, cfg)
44756             case 'ScrollPanel':  // ContentPanel (el, cfg)
44757                 if(cfg.autoCreate) {
44758                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44759                 } else {
44760                     var el = this.el.createChild();
44761                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44762                 }
44763                 
44764                 this.add(region, ret);
44765                 break;
44766             
44767             
44768             case 'TreePanel': // our new panel!
44769                 cfg.el = this.el.createChild();
44770                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44771                 this.add(region, ret);
44772                 break;
44773             
44774             case 'NestedLayoutPanel': 
44775                 // create a new Layout (which is  a Border Layout...
44776                 var el = this.el.createChild();
44777                 var clayout = cfg.layout;
44778                 delete cfg.layout;
44779                 clayout.items   = clayout.items  || [];
44780                 // replace this exitems with the clayout ones..
44781                 xitems = clayout.items;
44782                  
44783                 
44784                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44785                     cfg.background = false;
44786                 }
44787                 var layout = new Roo.BorderLayout(el, clayout);
44788                 
44789                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44790                 //console.log('adding nested layout panel '  + cfg.toSource());
44791                 this.add(region, ret);
44792                 nb = {}; /// find first...
44793                 break;
44794                 
44795             case 'GridPanel': 
44796             
44797                 // needs grid and region
44798                 
44799                 //var el = this.getRegion(region).el.createChild();
44800                 var el = this.el.createChild();
44801                 // create the grid first...
44802                 
44803                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44804                 delete cfg.grid;
44805                 if (region == 'center' && this.active ) {
44806                     cfg.background = false;
44807                 }
44808                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44809                 
44810                 this.add(region, ret);
44811                 if (cfg.background) {
44812                     ret.on('activate', function(gp) {
44813                         if (!gp.grid.rendered) {
44814                             gp.grid.render();
44815                         }
44816                     });
44817                 } else {
44818                     grid.render();
44819                 }
44820                 break;
44821            
44822                
44823                 
44824                 
44825             default: 
44826                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44827                 return null;
44828              // GridPanel (grid, cfg)
44829             
44830         }
44831         this.beginUpdate();
44832         // add children..
44833         var region = '';
44834         var abn = {};
44835         Roo.each(xitems, function(i)  {
44836             region = nb && i.region ? i.region : false;
44837             
44838             var add = ret.addxtype(i);
44839            
44840             if (region) {
44841                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44842                 if (!i.background) {
44843                     abn[region] = nb[region] ;
44844                 }
44845             }
44846             
44847         });
44848         this.endUpdate();
44849
44850         // make the last non-background panel active..
44851         //if (nb) { Roo.log(abn); }
44852         if (nb) {
44853             
44854             for(var r in abn) {
44855                 region = this.getRegion(r);
44856                 if (region) {
44857                     // tried using nb[r], but it does not work..
44858                      
44859                     region.showPanel(abn[r]);
44860                    
44861                 }
44862             }
44863         }
44864         return ret;
44865         
44866     }
44867 });
44868
44869 /**
44870  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44871  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44872  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44873  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44874  * <pre><code>
44875 // shorthand
44876 var CP = Roo.ContentPanel;
44877
44878 var layout = Roo.BorderLayout.create({
44879     north: {
44880         initialSize: 25,
44881         titlebar: false,
44882         panels: [new CP("north", "North")]
44883     },
44884     west: {
44885         split:true,
44886         initialSize: 200,
44887         minSize: 175,
44888         maxSize: 400,
44889         titlebar: true,
44890         collapsible: true,
44891         panels: [new CP("west", {title: "West"})]
44892     },
44893     east: {
44894         split:true,
44895         initialSize: 202,
44896         minSize: 175,
44897         maxSize: 400,
44898         titlebar: true,
44899         collapsible: true,
44900         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44901     },
44902     south: {
44903         split:true,
44904         initialSize: 100,
44905         minSize: 100,
44906         maxSize: 200,
44907         titlebar: true,
44908         collapsible: true,
44909         panels: [new CP("south", {title: "South", closable: true})]
44910     },
44911     center: {
44912         titlebar: true,
44913         autoScroll:true,
44914         resizeTabs: true,
44915         minTabWidth: 50,
44916         preferredTabWidth: 150,
44917         panels: [
44918             new CP("center1", {title: "Close Me", closable: true}),
44919             new CP("center2", {title: "Center Panel", closable: false})
44920         ]
44921     }
44922 }, document.body);
44923
44924 layout.getRegion("center").showPanel("center1");
44925 </code></pre>
44926  * @param config
44927  * @param targetEl
44928  */
44929 Roo.BorderLayout.create = function(config, targetEl){
44930     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44931     layout.beginUpdate();
44932     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44933     for(var j = 0, jlen = regions.length; j < jlen; j++){
44934         var lr = regions[j];
44935         if(layout.regions[lr] && config[lr].panels){
44936             var r = layout.regions[lr];
44937             var ps = config[lr].panels;
44938             layout.addTypedPanels(r, ps);
44939         }
44940     }
44941     layout.endUpdate();
44942     return layout;
44943 };
44944
44945 // private
44946 Roo.BorderLayout.RegionFactory = {
44947     // private
44948     validRegions : ["north","south","east","west","center"],
44949
44950     // private
44951     create : function(target, mgr, config){
44952         target = target.toLowerCase();
44953         if(config.lightweight || config.basic){
44954             return new Roo.BasicLayoutRegion(mgr, config, target);
44955         }
44956         switch(target){
44957             case "north":
44958                 return new Roo.NorthLayoutRegion(mgr, config);
44959             case "south":
44960                 return new Roo.SouthLayoutRegion(mgr, config);
44961             case "east":
44962                 return new Roo.EastLayoutRegion(mgr, config);
44963             case "west":
44964                 return new Roo.WestLayoutRegion(mgr, config);
44965             case "center":
44966                 return new Roo.CenterLayoutRegion(mgr, config);
44967         }
44968         throw 'Layout region "'+target+'" not supported.';
44969     }
44970 };/*
44971  * Based on:
44972  * Ext JS Library 1.1.1
44973  * Copyright(c) 2006-2007, Ext JS, LLC.
44974  *
44975  * Originally Released Under LGPL - original licence link has changed is not relivant.
44976  *
44977  * Fork - LGPL
44978  * <script type="text/javascript">
44979  */
44980  
44981 /**
44982  * @class Roo.BasicLayoutRegion
44983  * @extends Roo.util.Observable
44984  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44985  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44986  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44987  */
44988 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44989     this.mgr = mgr;
44990     this.position  = pos;
44991     this.events = {
44992         /**
44993          * @scope Roo.BasicLayoutRegion
44994          */
44995         
44996         /**
44997          * @event beforeremove
44998          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44999          * @param {Roo.LayoutRegion} this
45000          * @param {Roo.ContentPanel} panel The panel
45001          * @param {Object} e The cancel event object
45002          */
45003         "beforeremove" : true,
45004         /**
45005          * @event invalidated
45006          * Fires when the layout for this region is changed.
45007          * @param {Roo.LayoutRegion} this
45008          */
45009         "invalidated" : true,
45010         /**
45011          * @event visibilitychange
45012          * Fires when this region is shown or hidden 
45013          * @param {Roo.LayoutRegion} this
45014          * @param {Boolean} visibility true or false
45015          */
45016         "visibilitychange" : true,
45017         /**
45018          * @event paneladded
45019          * Fires when a panel is added. 
45020          * @param {Roo.LayoutRegion} this
45021          * @param {Roo.ContentPanel} panel The panel
45022          */
45023         "paneladded" : true,
45024         /**
45025          * @event panelremoved
45026          * Fires when a panel is removed. 
45027          * @param {Roo.LayoutRegion} this
45028          * @param {Roo.ContentPanel} panel The panel
45029          */
45030         "panelremoved" : true,
45031         /**
45032          * @event collapsed
45033          * Fires when this region is collapsed.
45034          * @param {Roo.LayoutRegion} this
45035          */
45036         "collapsed" : true,
45037         /**
45038          * @event expanded
45039          * Fires when this region is expanded.
45040          * @param {Roo.LayoutRegion} this
45041          */
45042         "expanded" : true,
45043         /**
45044          * @event slideshow
45045          * Fires when this region is slid into view.
45046          * @param {Roo.LayoutRegion} this
45047          */
45048         "slideshow" : true,
45049         /**
45050          * @event slidehide
45051          * Fires when this region slides out of view. 
45052          * @param {Roo.LayoutRegion} this
45053          */
45054         "slidehide" : true,
45055         /**
45056          * @event panelactivated
45057          * Fires when a panel is activated. 
45058          * @param {Roo.LayoutRegion} this
45059          * @param {Roo.ContentPanel} panel The activated panel
45060          */
45061         "panelactivated" : true,
45062         /**
45063          * @event resized
45064          * Fires when the user resizes this region. 
45065          * @param {Roo.LayoutRegion} this
45066          * @param {Number} newSize The new size (width for east/west, height for north/south)
45067          */
45068         "resized" : true
45069     };
45070     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45071     this.panels = new Roo.util.MixedCollection();
45072     this.panels.getKey = this.getPanelId.createDelegate(this);
45073     this.box = null;
45074     this.activePanel = null;
45075     // ensure listeners are added...
45076     
45077     if (config.listeners || config.events) {
45078         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45079             listeners : config.listeners || {},
45080             events : config.events || {}
45081         });
45082     }
45083     
45084     if(skipConfig !== true){
45085         this.applyConfig(config);
45086     }
45087 };
45088
45089 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45090     getPanelId : function(p){
45091         return p.getId();
45092     },
45093     
45094     applyConfig : function(config){
45095         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45096         this.config = config;
45097         
45098     },
45099     
45100     /**
45101      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45102      * the width, for horizontal (north, south) the height.
45103      * @param {Number} newSize The new width or height
45104      */
45105     resizeTo : function(newSize){
45106         var el = this.el ? this.el :
45107                  (this.activePanel ? this.activePanel.getEl() : null);
45108         if(el){
45109             switch(this.position){
45110                 case "east":
45111                 case "west":
45112                     el.setWidth(newSize);
45113                     this.fireEvent("resized", this, newSize);
45114                 break;
45115                 case "north":
45116                 case "south":
45117                     el.setHeight(newSize);
45118                     this.fireEvent("resized", this, newSize);
45119                 break;                
45120             }
45121         }
45122     },
45123     
45124     getBox : function(){
45125         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45126     },
45127     
45128     getMargins : function(){
45129         return this.margins;
45130     },
45131     
45132     updateBox : function(box){
45133         this.box = box;
45134         var el = this.activePanel.getEl();
45135         el.dom.style.left = box.x + "px";
45136         el.dom.style.top = box.y + "px";
45137         this.activePanel.setSize(box.width, box.height);
45138     },
45139     
45140     /**
45141      * Returns the container element for this region.
45142      * @return {Roo.Element}
45143      */
45144     getEl : function(){
45145         return this.activePanel;
45146     },
45147     
45148     /**
45149      * Returns true if this region is currently visible.
45150      * @return {Boolean}
45151      */
45152     isVisible : function(){
45153         return this.activePanel ? true : false;
45154     },
45155     
45156     setActivePanel : function(panel){
45157         panel = this.getPanel(panel);
45158         if(this.activePanel && this.activePanel != panel){
45159             this.activePanel.setActiveState(false);
45160             this.activePanel.getEl().setLeftTop(-10000,-10000);
45161         }
45162         this.activePanel = panel;
45163         panel.setActiveState(true);
45164         if(this.box){
45165             panel.setSize(this.box.width, this.box.height);
45166         }
45167         this.fireEvent("panelactivated", this, panel);
45168         this.fireEvent("invalidated");
45169     },
45170     
45171     /**
45172      * Show the specified panel.
45173      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45174      * @return {Roo.ContentPanel} The shown panel or null
45175      */
45176     showPanel : function(panel){
45177         if(panel = this.getPanel(panel)){
45178             this.setActivePanel(panel);
45179         }
45180         return panel;
45181     },
45182     
45183     /**
45184      * Get the active panel for this region.
45185      * @return {Roo.ContentPanel} The active panel or null
45186      */
45187     getActivePanel : function(){
45188         return this.activePanel;
45189     },
45190     
45191     /**
45192      * Add the passed ContentPanel(s)
45193      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45194      * @return {Roo.ContentPanel} The panel added (if only one was added)
45195      */
45196     add : function(panel){
45197         if(arguments.length > 1){
45198             for(var i = 0, len = arguments.length; i < len; i++) {
45199                 this.add(arguments[i]);
45200             }
45201             return null;
45202         }
45203         if(this.hasPanel(panel)){
45204             this.showPanel(panel);
45205             return panel;
45206         }
45207         var el = panel.getEl();
45208         if(el.dom.parentNode != this.mgr.el.dom){
45209             this.mgr.el.dom.appendChild(el.dom);
45210         }
45211         if(panel.setRegion){
45212             panel.setRegion(this);
45213         }
45214         this.panels.add(panel);
45215         el.setStyle("position", "absolute");
45216         if(!panel.background){
45217             this.setActivePanel(panel);
45218             if(this.config.initialSize && this.panels.getCount()==1){
45219                 this.resizeTo(this.config.initialSize);
45220             }
45221         }
45222         this.fireEvent("paneladded", this, panel);
45223         return panel;
45224     },
45225     
45226     /**
45227      * Returns true if the panel is in this region.
45228      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45229      * @return {Boolean}
45230      */
45231     hasPanel : function(panel){
45232         if(typeof panel == "object"){ // must be panel obj
45233             panel = panel.getId();
45234         }
45235         return this.getPanel(panel) ? true : false;
45236     },
45237     
45238     /**
45239      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45240      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45241      * @param {Boolean} preservePanel Overrides the config preservePanel option
45242      * @return {Roo.ContentPanel} The panel that was removed
45243      */
45244     remove : function(panel, preservePanel){
45245         panel = this.getPanel(panel);
45246         if(!panel){
45247             return null;
45248         }
45249         var e = {};
45250         this.fireEvent("beforeremove", this, panel, e);
45251         if(e.cancel === true){
45252             return null;
45253         }
45254         var panelId = panel.getId();
45255         this.panels.removeKey(panelId);
45256         return panel;
45257     },
45258     
45259     /**
45260      * Returns the panel specified or null if it's not in this region.
45261      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45262      * @return {Roo.ContentPanel}
45263      */
45264     getPanel : function(id){
45265         if(typeof id == "object"){ // must be panel obj
45266             return id;
45267         }
45268         return this.panels.get(id);
45269     },
45270     
45271     /**
45272      * Returns this regions position (north/south/east/west/center).
45273      * @return {String} 
45274      */
45275     getPosition: function(){
45276         return this.position;    
45277     }
45278 });/*
45279  * Based on:
45280  * Ext JS Library 1.1.1
45281  * Copyright(c) 2006-2007, Ext JS, LLC.
45282  *
45283  * Originally Released Under LGPL - original licence link has changed is not relivant.
45284  *
45285  * Fork - LGPL
45286  * <script type="text/javascript">
45287  */
45288  
45289 /**
45290  * @class Roo.LayoutRegion
45291  * @extends Roo.BasicLayoutRegion
45292  * This class represents a region in a layout manager.
45293  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45294  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45295  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45296  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45297  * @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})
45298  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45299  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45300  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45301  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45302  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45303  * @cfg {String}    title           The title for the region (overrides panel titles)
45304  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45305  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45306  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45307  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45308  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45309  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45310  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45311  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45312  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45313  * @cfg {Boolean}   showPin         True to show a pin button
45314  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45315  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45316  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45317  * @cfg {Number}    width           For East/West panels
45318  * @cfg {Number}    height          For North/South panels
45319  * @cfg {Boolean}   split           To show the splitter
45320  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45321  */
45322 Roo.LayoutRegion = function(mgr, config, pos){
45323     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45324     var dh = Roo.DomHelper;
45325     /** This region's container element 
45326     * @type Roo.Element */
45327     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45328     /** This region's title element 
45329     * @type Roo.Element */
45330
45331     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45332         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45333         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45334     ]}, true);
45335     this.titleEl.enableDisplayMode();
45336     /** This region's title text element 
45337     * @type HTMLElement */
45338     this.titleTextEl = this.titleEl.dom.firstChild;
45339     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45340     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45341     this.closeBtn.enableDisplayMode();
45342     this.closeBtn.on("click", this.closeClicked, this);
45343     this.closeBtn.hide();
45344
45345     this.createBody(config);
45346     this.visible = true;
45347     this.collapsed = false;
45348
45349     if(config.hideWhenEmpty){
45350         this.hide();
45351         this.on("paneladded", this.validateVisibility, this);
45352         this.on("panelremoved", this.validateVisibility, this);
45353     }
45354     this.applyConfig(config);
45355 };
45356
45357 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45358
45359     createBody : function(){
45360         /** This region's body element 
45361         * @type Roo.Element */
45362         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45363     },
45364
45365     applyConfig : function(c){
45366         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45367             var dh = Roo.DomHelper;
45368             if(c.titlebar !== false){
45369                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45370                 this.collapseBtn.on("click", this.collapse, this);
45371                 this.collapseBtn.enableDisplayMode();
45372
45373                 if(c.showPin === true || this.showPin){
45374                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45375                     this.stickBtn.enableDisplayMode();
45376                     this.stickBtn.on("click", this.expand, this);
45377                     this.stickBtn.hide();
45378                 }
45379             }
45380             /** This region's collapsed element
45381             * @type Roo.Element */
45382             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45383                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45384             ]}, true);
45385             if(c.floatable !== false){
45386                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45387                this.collapsedEl.on("click", this.collapseClick, this);
45388             }
45389
45390             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45391                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45392                    id: "message", unselectable: "on", style:{"float":"left"}});
45393                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45394              }
45395             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45396             this.expandBtn.on("click", this.expand, this);
45397         }
45398         if(this.collapseBtn){
45399             this.collapseBtn.setVisible(c.collapsible == true);
45400         }
45401         this.cmargins = c.cmargins || this.cmargins ||
45402                          (this.position == "west" || this.position == "east" ?
45403                              {top: 0, left: 2, right:2, bottom: 0} :
45404                              {top: 2, left: 0, right:0, bottom: 2});
45405         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45406         this.bottomTabs = c.tabPosition != "top";
45407         this.autoScroll = c.autoScroll || false;
45408         if(this.autoScroll){
45409             this.bodyEl.setStyle("overflow", "auto");
45410         }else{
45411             this.bodyEl.setStyle("overflow", "hidden");
45412         }
45413         //if(c.titlebar !== false){
45414             if((!c.titlebar && !c.title) || c.titlebar === false){
45415                 this.titleEl.hide();
45416             }else{
45417                 this.titleEl.show();
45418                 if(c.title){
45419                     this.titleTextEl.innerHTML = c.title;
45420                 }
45421             }
45422         //}
45423         this.duration = c.duration || .30;
45424         this.slideDuration = c.slideDuration || .45;
45425         this.config = c;
45426         if(c.collapsed){
45427             this.collapse(true);
45428         }
45429         if(c.hidden){
45430             this.hide();
45431         }
45432     },
45433     /**
45434      * Returns true if this region is currently visible.
45435      * @return {Boolean}
45436      */
45437     isVisible : function(){
45438         return this.visible;
45439     },
45440
45441     /**
45442      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45443      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45444      */
45445     setCollapsedTitle : function(title){
45446         title = title || "&#160;";
45447         if(this.collapsedTitleTextEl){
45448             this.collapsedTitleTextEl.innerHTML = title;
45449         }
45450     },
45451
45452     getBox : function(){
45453         var b;
45454         if(!this.collapsed){
45455             b = this.el.getBox(false, true);
45456         }else{
45457             b = this.collapsedEl.getBox(false, true);
45458         }
45459         return b;
45460     },
45461
45462     getMargins : function(){
45463         return this.collapsed ? this.cmargins : this.margins;
45464     },
45465
45466     highlight : function(){
45467         this.el.addClass("x-layout-panel-dragover");
45468     },
45469
45470     unhighlight : function(){
45471         this.el.removeClass("x-layout-panel-dragover");
45472     },
45473
45474     updateBox : function(box){
45475         this.box = box;
45476         if(!this.collapsed){
45477             this.el.dom.style.left = box.x + "px";
45478             this.el.dom.style.top = box.y + "px";
45479             this.updateBody(box.width, box.height);
45480         }else{
45481             this.collapsedEl.dom.style.left = box.x + "px";
45482             this.collapsedEl.dom.style.top = box.y + "px";
45483             this.collapsedEl.setSize(box.width, box.height);
45484         }
45485         if(this.tabs){
45486             this.tabs.autoSizeTabs();
45487         }
45488     },
45489
45490     updateBody : function(w, h){
45491         if(w !== null){
45492             this.el.setWidth(w);
45493             w -= this.el.getBorderWidth("rl");
45494             if(this.config.adjustments){
45495                 w += this.config.adjustments[0];
45496             }
45497         }
45498         if(h !== null){
45499             this.el.setHeight(h);
45500             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45501             h -= this.el.getBorderWidth("tb");
45502             if(this.config.adjustments){
45503                 h += this.config.adjustments[1];
45504             }
45505             this.bodyEl.setHeight(h);
45506             if(this.tabs){
45507                 h = this.tabs.syncHeight(h);
45508             }
45509         }
45510         if(this.panelSize){
45511             w = w !== null ? w : this.panelSize.width;
45512             h = h !== null ? h : this.panelSize.height;
45513         }
45514         if(this.activePanel){
45515             var el = this.activePanel.getEl();
45516             w = w !== null ? w : el.getWidth();
45517             h = h !== null ? h : el.getHeight();
45518             this.panelSize = {width: w, height: h};
45519             this.activePanel.setSize(w, h);
45520         }
45521         if(Roo.isIE && this.tabs){
45522             this.tabs.el.repaint();
45523         }
45524     },
45525
45526     /**
45527      * Returns the container element for this region.
45528      * @return {Roo.Element}
45529      */
45530     getEl : function(){
45531         return this.el;
45532     },
45533
45534     /**
45535      * Hides this region.
45536      */
45537     hide : function(){
45538         if(!this.collapsed){
45539             this.el.dom.style.left = "-2000px";
45540             this.el.hide();
45541         }else{
45542             this.collapsedEl.dom.style.left = "-2000px";
45543             this.collapsedEl.hide();
45544         }
45545         this.visible = false;
45546         this.fireEvent("visibilitychange", this, false);
45547     },
45548
45549     /**
45550      * Shows this region if it was previously hidden.
45551      */
45552     show : function(){
45553         if(!this.collapsed){
45554             this.el.show();
45555         }else{
45556             this.collapsedEl.show();
45557         }
45558         this.visible = true;
45559         this.fireEvent("visibilitychange", this, true);
45560     },
45561
45562     closeClicked : function(){
45563         if(this.activePanel){
45564             this.remove(this.activePanel);
45565         }
45566     },
45567
45568     collapseClick : function(e){
45569         if(this.isSlid){
45570            e.stopPropagation();
45571            this.slideIn();
45572         }else{
45573            e.stopPropagation();
45574            this.slideOut();
45575         }
45576     },
45577
45578     /**
45579      * Collapses this region.
45580      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45581      */
45582     collapse : function(skipAnim){
45583         if(this.collapsed) return;
45584         this.collapsed = true;
45585         if(this.split){
45586             this.split.el.hide();
45587         }
45588         if(this.config.animate && skipAnim !== true){
45589             this.fireEvent("invalidated", this);
45590             this.animateCollapse();
45591         }else{
45592             this.el.setLocation(-20000,-20000);
45593             this.el.hide();
45594             this.collapsedEl.show();
45595             this.fireEvent("collapsed", this);
45596             this.fireEvent("invalidated", this);
45597         }
45598     },
45599
45600     animateCollapse : function(){
45601         // overridden
45602     },
45603
45604     /**
45605      * Expands this region if it was previously collapsed.
45606      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45607      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45608      */
45609     expand : function(e, skipAnim){
45610         if(e) e.stopPropagation();
45611         if(!this.collapsed || this.el.hasActiveFx()) return;
45612         if(this.isSlid){
45613             this.afterSlideIn();
45614             skipAnim = true;
45615         }
45616         this.collapsed = false;
45617         if(this.config.animate && skipAnim !== true){
45618             this.animateExpand();
45619         }else{
45620             this.el.show();
45621             if(this.split){
45622                 this.split.el.show();
45623             }
45624             this.collapsedEl.setLocation(-2000,-2000);
45625             this.collapsedEl.hide();
45626             this.fireEvent("invalidated", this);
45627             this.fireEvent("expanded", this);
45628         }
45629     },
45630
45631     animateExpand : function(){
45632         // overridden
45633     },
45634
45635     initTabs : function()
45636     {
45637         this.bodyEl.setStyle("overflow", "hidden");
45638         var ts = new Roo.TabPanel(
45639                 this.bodyEl.dom,
45640                 {
45641                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45642                     disableTooltips: this.config.disableTabTips,
45643                     toolbar : this.config.toolbar
45644                 }
45645         );
45646         if(this.config.hideTabs){
45647             ts.stripWrap.setDisplayed(false);
45648         }
45649         this.tabs = ts;
45650         ts.resizeTabs = this.config.resizeTabs === true;
45651         ts.minTabWidth = this.config.minTabWidth || 40;
45652         ts.maxTabWidth = this.config.maxTabWidth || 250;
45653         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45654         ts.monitorResize = false;
45655         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45656         ts.bodyEl.addClass('x-layout-tabs-body');
45657         this.panels.each(this.initPanelAsTab, this);
45658     },
45659
45660     initPanelAsTab : function(panel){
45661         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45662                     this.config.closeOnTab && panel.isClosable());
45663         if(panel.tabTip !== undefined){
45664             ti.setTooltip(panel.tabTip);
45665         }
45666         ti.on("activate", function(){
45667               this.setActivePanel(panel);
45668         }, this);
45669         if(this.config.closeOnTab){
45670             ti.on("beforeclose", function(t, e){
45671                 e.cancel = true;
45672                 this.remove(panel);
45673             }, this);
45674         }
45675         return ti;
45676     },
45677
45678     updatePanelTitle : function(panel, title){
45679         if(this.activePanel == panel){
45680             this.updateTitle(title);
45681         }
45682         if(this.tabs){
45683             var ti = this.tabs.getTab(panel.getEl().id);
45684             ti.setText(title);
45685             if(panel.tabTip !== undefined){
45686                 ti.setTooltip(panel.tabTip);
45687             }
45688         }
45689     },
45690
45691     updateTitle : function(title){
45692         if(this.titleTextEl && !this.config.title){
45693             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45694         }
45695     },
45696
45697     setActivePanel : function(panel){
45698         panel = this.getPanel(panel);
45699         if(this.activePanel && this.activePanel != panel){
45700             this.activePanel.setActiveState(false);
45701         }
45702         this.activePanel = panel;
45703         panel.setActiveState(true);
45704         if(this.panelSize){
45705             panel.setSize(this.panelSize.width, this.panelSize.height);
45706         }
45707         if(this.closeBtn){
45708             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45709         }
45710         this.updateTitle(panel.getTitle());
45711         if(this.tabs){
45712             this.fireEvent("invalidated", this);
45713         }
45714         this.fireEvent("panelactivated", this, panel);
45715     },
45716
45717     /**
45718      * Shows the specified panel.
45719      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45720      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45721      */
45722     showPanel : function(panel){
45723         if(panel = this.getPanel(panel)){
45724             if(this.tabs){
45725                 var tab = this.tabs.getTab(panel.getEl().id);
45726                 if(tab.isHidden()){
45727                     this.tabs.unhideTab(tab.id);
45728                 }
45729                 tab.activate();
45730             }else{
45731                 this.setActivePanel(panel);
45732             }
45733         }
45734         return panel;
45735     },
45736
45737     /**
45738      * Get the active panel for this region.
45739      * @return {Roo.ContentPanel} The active panel or null
45740      */
45741     getActivePanel : function(){
45742         return this.activePanel;
45743     },
45744
45745     validateVisibility : function(){
45746         if(this.panels.getCount() < 1){
45747             this.updateTitle("&#160;");
45748             this.closeBtn.hide();
45749             this.hide();
45750         }else{
45751             if(!this.isVisible()){
45752                 this.show();
45753             }
45754         }
45755     },
45756
45757     /**
45758      * Adds the passed ContentPanel(s) to this region.
45759      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45760      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45761      */
45762     add : function(panel){
45763         if(arguments.length > 1){
45764             for(var i = 0, len = arguments.length; i < len; i++) {
45765                 this.add(arguments[i]);
45766             }
45767             return null;
45768         }
45769         if(this.hasPanel(panel)){
45770             this.showPanel(panel);
45771             return panel;
45772         }
45773         panel.setRegion(this);
45774         this.panels.add(panel);
45775         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45776             this.bodyEl.dom.appendChild(panel.getEl().dom);
45777             if(panel.background !== true){
45778                 this.setActivePanel(panel);
45779             }
45780             this.fireEvent("paneladded", this, panel);
45781             return panel;
45782         }
45783         if(!this.tabs){
45784             this.initTabs();
45785         }else{
45786             this.initPanelAsTab(panel);
45787         }
45788         if(panel.background !== true){
45789             this.tabs.activate(panel.getEl().id);
45790         }
45791         this.fireEvent("paneladded", this, panel);
45792         return panel;
45793     },
45794
45795     /**
45796      * Hides the tab for the specified panel.
45797      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45798      */
45799     hidePanel : function(panel){
45800         if(this.tabs && (panel = this.getPanel(panel))){
45801             this.tabs.hideTab(panel.getEl().id);
45802         }
45803     },
45804
45805     /**
45806      * Unhides the tab for a previously hidden panel.
45807      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45808      */
45809     unhidePanel : function(panel){
45810         if(this.tabs && (panel = this.getPanel(panel))){
45811             this.tabs.unhideTab(panel.getEl().id);
45812         }
45813     },
45814
45815     clearPanels : function(){
45816         while(this.panels.getCount() > 0){
45817              this.remove(this.panels.first());
45818         }
45819     },
45820
45821     /**
45822      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45823      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45824      * @param {Boolean} preservePanel Overrides the config preservePanel option
45825      * @return {Roo.ContentPanel} The panel that was removed
45826      */
45827     remove : function(panel, preservePanel){
45828         panel = this.getPanel(panel);
45829         if(!panel){
45830             return null;
45831         }
45832         var e = {};
45833         this.fireEvent("beforeremove", this, panel, e);
45834         if(e.cancel === true){
45835             return null;
45836         }
45837         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45838         var panelId = panel.getId();
45839         this.panels.removeKey(panelId);
45840         if(preservePanel){
45841             document.body.appendChild(panel.getEl().dom);
45842         }
45843         if(this.tabs){
45844             this.tabs.removeTab(panel.getEl().id);
45845         }else if (!preservePanel){
45846             this.bodyEl.dom.removeChild(panel.getEl().dom);
45847         }
45848         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45849             var p = this.panels.first();
45850             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45851             tempEl.appendChild(p.getEl().dom);
45852             this.bodyEl.update("");
45853             this.bodyEl.dom.appendChild(p.getEl().dom);
45854             tempEl = null;
45855             this.updateTitle(p.getTitle());
45856             this.tabs = null;
45857             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45858             this.setActivePanel(p);
45859         }
45860         panel.setRegion(null);
45861         if(this.activePanel == panel){
45862             this.activePanel = null;
45863         }
45864         if(this.config.autoDestroy !== false && preservePanel !== true){
45865             try{panel.destroy();}catch(e){}
45866         }
45867         this.fireEvent("panelremoved", this, panel);
45868         return panel;
45869     },
45870
45871     /**
45872      * Returns the TabPanel component used by this region
45873      * @return {Roo.TabPanel}
45874      */
45875     getTabs : function(){
45876         return this.tabs;
45877     },
45878
45879     createTool : function(parentEl, className){
45880         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45881             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45882         btn.addClassOnOver("x-layout-tools-button-over");
45883         return btn;
45884     }
45885 });/*
45886  * Based on:
45887  * Ext JS Library 1.1.1
45888  * Copyright(c) 2006-2007, Ext JS, LLC.
45889  *
45890  * Originally Released Under LGPL - original licence link has changed is not relivant.
45891  *
45892  * Fork - LGPL
45893  * <script type="text/javascript">
45894  */
45895  
45896
45897
45898 /**
45899  * @class Roo.SplitLayoutRegion
45900  * @extends Roo.LayoutRegion
45901  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45902  */
45903 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45904     this.cursor = cursor;
45905     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45906 };
45907
45908 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45909     splitTip : "Drag to resize.",
45910     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45911     useSplitTips : false,
45912
45913     applyConfig : function(config){
45914         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45915         if(config.split){
45916             if(!this.split){
45917                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45918                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45919                 /** The SplitBar for this region 
45920                 * @type Roo.SplitBar */
45921                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45922                 this.split.on("moved", this.onSplitMove, this);
45923                 this.split.useShim = config.useShim === true;
45924                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45925                 if(this.useSplitTips){
45926                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45927                 }
45928                 if(config.collapsible){
45929                     this.split.el.on("dblclick", this.collapse,  this);
45930                 }
45931             }
45932             if(typeof config.minSize != "undefined"){
45933                 this.split.minSize = config.minSize;
45934             }
45935             if(typeof config.maxSize != "undefined"){
45936                 this.split.maxSize = config.maxSize;
45937             }
45938             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45939                 this.hideSplitter();
45940             }
45941         }
45942     },
45943
45944     getHMaxSize : function(){
45945          var cmax = this.config.maxSize || 10000;
45946          var center = this.mgr.getRegion("center");
45947          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45948     },
45949
45950     getVMaxSize : function(){
45951          var cmax = this.config.maxSize || 10000;
45952          var center = this.mgr.getRegion("center");
45953          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45954     },
45955
45956     onSplitMove : function(split, newSize){
45957         this.fireEvent("resized", this, newSize);
45958     },
45959     
45960     /** 
45961      * Returns the {@link Roo.SplitBar} for this region.
45962      * @return {Roo.SplitBar}
45963      */
45964     getSplitBar : function(){
45965         return this.split;
45966     },
45967     
45968     hide : function(){
45969         this.hideSplitter();
45970         Roo.SplitLayoutRegion.superclass.hide.call(this);
45971     },
45972
45973     hideSplitter : function(){
45974         if(this.split){
45975             this.split.el.setLocation(-2000,-2000);
45976             this.split.el.hide();
45977         }
45978     },
45979
45980     show : function(){
45981         if(this.split){
45982             this.split.el.show();
45983         }
45984         Roo.SplitLayoutRegion.superclass.show.call(this);
45985     },
45986     
45987     beforeSlide: function(){
45988         if(Roo.isGecko){// firefox overflow auto bug workaround
45989             this.bodyEl.clip();
45990             if(this.tabs) this.tabs.bodyEl.clip();
45991             if(this.activePanel){
45992                 this.activePanel.getEl().clip();
45993                 
45994                 if(this.activePanel.beforeSlide){
45995                     this.activePanel.beforeSlide();
45996                 }
45997             }
45998         }
45999     },
46000     
46001     afterSlide : function(){
46002         if(Roo.isGecko){// firefox overflow auto bug workaround
46003             this.bodyEl.unclip();
46004             if(this.tabs) this.tabs.bodyEl.unclip();
46005             if(this.activePanel){
46006                 this.activePanel.getEl().unclip();
46007                 if(this.activePanel.afterSlide){
46008                     this.activePanel.afterSlide();
46009                 }
46010             }
46011         }
46012     },
46013
46014     initAutoHide : function(){
46015         if(this.autoHide !== false){
46016             if(!this.autoHideHd){
46017                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46018                 this.autoHideHd = {
46019                     "mouseout": function(e){
46020                         if(!e.within(this.el, true)){
46021                             st.delay(500);
46022                         }
46023                     },
46024                     "mouseover" : function(e){
46025                         st.cancel();
46026                     },
46027                     scope : this
46028                 };
46029             }
46030             this.el.on(this.autoHideHd);
46031         }
46032     },
46033
46034     clearAutoHide : function(){
46035         if(this.autoHide !== false){
46036             this.el.un("mouseout", this.autoHideHd.mouseout);
46037             this.el.un("mouseover", this.autoHideHd.mouseover);
46038         }
46039     },
46040
46041     clearMonitor : function(){
46042         Roo.get(document).un("click", this.slideInIf, this);
46043     },
46044
46045     // these names are backwards but not changed for compat
46046     slideOut : function(){
46047         if(this.isSlid || this.el.hasActiveFx()){
46048             return;
46049         }
46050         this.isSlid = true;
46051         if(this.collapseBtn){
46052             this.collapseBtn.hide();
46053         }
46054         this.closeBtnState = this.closeBtn.getStyle('display');
46055         this.closeBtn.hide();
46056         if(this.stickBtn){
46057             this.stickBtn.show();
46058         }
46059         this.el.show();
46060         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46061         this.beforeSlide();
46062         this.el.setStyle("z-index", 10001);
46063         this.el.slideIn(this.getSlideAnchor(), {
46064             callback: function(){
46065                 this.afterSlide();
46066                 this.initAutoHide();
46067                 Roo.get(document).on("click", this.slideInIf, this);
46068                 this.fireEvent("slideshow", this);
46069             },
46070             scope: this,
46071             block: true
46072         });
46073     },
46074
46075     afterSlideIn : function(){
46076         this.clearAutoHide();
46077         this.isSlid = false;
46078         this.clearMonitor();
46079         this.el.setStyle("z-index", "");
46080         if(this.collapseBtn){
46081             this.collapseBtn.show();
46082         }
46083         this.closeBtn.setStyle('display', this.closeBtnState);
46084         if(this.stickBtn){
46085             this.stickBtn.hide();
46086         }
46087         this.fireEvent("slidehide", this);
46088     },
46089
46090     slideIn : function(cb){
46091         if(!this.isSlid || this.el.hasActiveFx()){
46092             Roo.callback(cb);
46093             return;
46094         }
46095         this.isSlid = false;
46096         this.beforeSlide();
46097         this.el.slideOut(this.getSlideAnchor(), {
46098             callback: function(){
46099                 this.el.setLeftTop(-10000, -10000);
46100                 this.afterSlide();
46101                 this.afterSlideIn();
46102                 Roo.callback(cb);
46103             },
46104             scope: this,
46105             block: true
46106         });
46107     },
46108     
46109     slideInIf : function(e){
46110         if(!e.within(this.el)){
46111             this.slideIn();
46112         }
46113     },
46114
46115     animateCollapse : function(){
46116         this.beforeSlide();
46117         this.el.setStyle("z-index", 20000);
46118         var anchor = this.getSlideAnchor();
46119         this.el.slideOut(anchor, {
46120             callback : function(){
46121                 this.el.setStyle("z-index", "");
46122                 this.collapsedEl.slideIn(anchor, {duration:.3});
46123                 this.afterSlide();
46124                 this.el.setLocation(-10000,-10000);
46125                 this.el.hide();
46126                 this.fireEvent("collapsed", this);
46127             },
46128             scope: this,
46129             block: true
46130         });
46131     },
46132
46133     animateExpand : function(){
46134         this.beforeSlide();
46135         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46136         this.el.setStyle("z-index", 20000);
46137         this.collapsedEl.hide({
46138             duration:.1
46139         });
46140         this.el.slideIn(this.getSlideAnchor(), {
46141             callback : function(){
46142                 this.el.setStyle("z-index", "");
46143                 this.afterSlide();
46144                 if(this.split){
46145                     this.split.el.show();
46146                 }
46147                 this.fireEvent("invalidated", this);
46148                 this.fireEvent("expanded", this);
46149             },
46150             scope: this,
46151             block: true
46152         });
46153     },
46154
46155     anchors : {
46156         "west" : "left",
46157         "east" : "right",
46158         "north" : "top",
46159         "south" : "bottom"
46160     },
46161
46162     sanchors : {
46163         "west" : "l",
46164         "east" : "r",
46165         "north" : "t",
46166         "south" : "b"
46167     },
46168
46169     canchors : {
46170         "west" : "tl-tr",
46171         "east" : "tr-tl",
46172         "north" : "tl-bl",
46173         "south" : "bl-tl"
46174     },
46175
46176     getAnchor : function(){
46177         return this.anchors[this.position];
46178     },
46179
46180     getCollapseAnchor : function(){
46181         return this.canchors[this.position];
46182     },
46183
46184     getSlideAnchor : function(){
46185         return this.sanchors[this.position];
46186     },
46187
46188     getAlignAdj : function(){
46189         var cm = this.cmargins;
46190         switch(this.position){
46191             case "west":
46192                 return [0, 0];
46193             break;
46194             case "east":
46195                 return [0, 0];
46196             break;
46197             case "north":
46198                 return [0, 0];
46199             break;
46200             case "south":
46201                 return [0, 0];
46202             break;
46203         }
46204     },
46205
46206     getExpandAdj : function(){
46207         var c = this.collapsedEl, cm = this.cmargins;
46208         switch(this.position){
46209             case "west":
46210                 return [-(cm.right+c.getWidth()+cm.left), 0];
46211             break;
46212             case "east":
46213                 return [cm.right+c.getWidth()+cm.left, 0];
46214             break;
46215             case "north":
46216                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46217             break;
46218             case "south":
46219                 return [0, cm.top+cm.bottom+c.getHeight()];
46220             break;
46221         }
46222     }
46223 });/*
46224  * Based on:
46225  * Ext JS Library 1.1.1
46226  * Copyright(c) 2006-2007, Ext JS, LLC.
46227  *
46228  * Originally Released Under LGPL - original licence link has changed is not relivant.
46229  *
46230  * Fork - LGPL
46231  * <script type="text/javascript">
46232  */
46233 /*
46234  * These classes are private internal classes
46235  */
46236 Roo.CenterLayoutRegion = function(mgr, config){
46237     Roo.LayoutRegion.call(this, mgr, config, "center");
46238     this.visible = true;
46239     this.minWidth = config.minWidth || 20;
46240     this.minHeight = config.minHeight || 20;
46241 };
46242
46243 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46244     hide : function(){
46245         // center panel can't be hidden
46246     },
46247     
46248     show : function(){
46249         // center panel can't be hidden
46250     },
46251     
46252     getMinWidth: function(){
46253         return this.minWidth;
46254     },
46255     
46256     getMinHeight: function(){
46257         return this.minHeight;
46258     }
46259 });
46260
46261
46262 Roo.NorthLayoutRegion = function(mgr, config){
46263     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46264     if(this.split){
46265         this.split.placement = Roo.SplitBar.TOP;
46266         this.split.orientation = Roo.SplitBar.VERTICAL;
46267         this.split.el.addClass("x-layout-split-v");
46268     }
46269     var size = config.initialSize || config.height;
46270     if(typeof size != "undefined"){
46271         this.el.setHeight(size);
46272     }
46273 };
46274 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46275     orientation: Roo.SplitBar.VERTICAL,
46276     getBox : function(){
46277         if(this.collapsed){
46278             return this.collapsedEl.getBox();
46279         }
46280         var box = this.el.getBox();
46281         if(this.split){
46282             box.height += this.split.el.getHeight();
46283         }
46284         return box;
46285     },
46286     
46287     updateBox : function(box){
46288         if(this.split && !this.collapsed){
46289             box.height -= this.split.el.getHeight();
46290             this.split.el.setLeft(box.x);
46291             this.split.el.setTop(box.y+box.height);
46292             this.split.el.setWidth(box.width);
46293         }
46294         if(this.collapsed){
46295             this.updateBody(box.width, null);
46296         }
46297         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46298     }
46299 });
46300
46301 Roo.SouthLayoutRegion = function(mgr, config){
46302     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46303     if(this.split){
46304         this.split.placement = Roo.SplitBar.BOTTOM;
46305         this.split.orientation = Roo.SplitBar.VERTICAL;
46306         this.split.el.addClass("x-layout-split-v");
46307     }
46308     var size = config.initialSize || config.height;
46309     if(typeof size != "undefined"){
46310         this.el.setHeight(size);
46311     }
46312 };
46313 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46314     orientation: Roo.SplitBar.VERTICAL,
46315     getBox : function(){
46316         if(this.collapsed){
46317             return this.collapsedEl.getBox();
46318         }
46319         var box = this.el.getBox();
46320         if(this.split){
46321             var sh = this.split.el.getHeight();
46322             box.height += sh;
46323             box.y -= sh;
46324         }
46325         return box;
46326     },
46327     
46328     updateBox : function(box){
46329         if(this.split && !this.collapsed){
46330             var sh = this.split.el.getHeight();
46331             box.height -= sh;
46332             box.y += sh;
46333             this.split.el.setLeft(box.x);
46334             this.split.el.setTop(box.y-sh);
46335             this.split.el.setWidth(box.width);
46336         }
46337         if(this.collapsed){
46338             this.updateBody(box.width, null);
46339         }
46340         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46341     }
46342 });
46343
46344 Roo.EastLayoutRegion = function(mgr, config){
46345     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46346     if(this.split){
46347         this.split.placement = Roo.SplitBar.RIGHT;
46348         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46349         this.split.el.addClass("x-layout-split-h");
46350     }
46351     var size = config.initialSize || config.width;
46352     if(typeof size != "undefined"){
46353         this.el.setWidth(size);
46354     }
46355 };
46356 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46357     orientation: Roo.SplitBar.HORIZONTAL,
46358     getBox : function(){
46359         if(this.collapsed){
46360             return this.collapsedEl.getBox();
46361         }
46362         var box = this.el.getBox();
46363         if(this.split){
46364             var sw = this.split.el.getWidth();
46365             box.width += sw;
46366             box.x -= sw;
46367         }
46368         return box;
46369     },
46370
46371     updateBox : function(box){
46372         if(this.split && !this.collapsed){
46373             var sw = this.split.el.getWidth();
46374             box.width -= sw;
46375             this.split.el.setLeft(box.x);
46376             this.split.el.setTop(box.y);
46377             this.split.el.setHeight(box.height);
46378             box.x += sw;
46379         }
46380         if(this.collapsed){
46381             this.updateBody(null, box.height);
46382         }
46383         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46384     }
46385 });
46386
46387 Roo.WestLayoutRegion = function(mgr, config){
46388     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46389     if(this.split){
46390         this.split.placement = Roo.SplitBar.LEFT;
46391         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46392         this.split.el.addClass("x-layout-split-h");
46393     }
46394     var size = config.initialSize || config.width;
46395     if(typeof size != "undefined"){
46396         this.el.setWidth(size);
46397     }
46398 };
46399 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46400     orientation: Roo.SplitBar.HORIZONTAL,
46401     getBox : function(){
46402         if(this.collapsed){
46403             return this.collapsedEl.getBox();
46404         }
46405         var box = this.el.getBox();
46406         if(this.split){
46407             box.width += this.split.el.getWidth();
46408         }
46409         return box;
46410     },
46411     
46412     updateBox : function(box){
46413         if(this.split && !this.collapsed){
46414             var sw = this.split.el.getWidth();
46415             box.width -= sw;
46416             this.split.el.setLeft(box.x+box.width);
46417             this.split.el.setTop(box.y);
46418             this.split.el.setHeight(box.height);
46419         }
46420         if(this.collapsed){
46421             this.updateBody(null, box.height);
46422         }
46423         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46424     }
46425 });
46426 /*
46427  * Based on:
46428  * Ext JS Library 1.1.1
46429  * Copyright(c) 2006-2007, Ext JS, LLC.
46430  *
46431  * Originally Released Under LGPL - original licence link has changed is not relivant.
46432  *
46433  * Fork - LGPL
46434  * <script type="text/javascript">
46435  */
46436  
46437  
46438 /*
46439  * Private internal class for reading and applying state
46440  */
46441 Roo.LayoutStateManager = function(layout){
46442      // default empty state
46443      this.state = {
46444         north: {},
46445         south: {},
46446         east: {},
46447         west: {}       
46448     };
46449 };
46450
46451 Roo.LayoutStateManager.prototype = {
46452     init : function(layout, provider){
46453         this.provider = provider;
46454         var state = provider.get(layout.id+"-layout-state");
46455         if(state){
46456             var wasUpdating = layout.isUpdating();
46457             if(!wasUpdating){
46458                 layout.beginUpdate();
46459             }
46460             for(var key in state){
46461                 if(typeof state[key] != "function"){
46462                     var rstate = state[key];
46463                     var r = layout.getRegion(key);
46464                     if(r && rstate){
46465                         if(rstate.size){
46466                             r.resizeTo(rstate.size);
46467                         }
46468                         if(rstate.collapsed == true){
46469                             r.collapse(true);
46470                         }else{
46471                             r.expand(null, true);
46472                         }
46473                     }
46474                 }
46475             }
46476             if(!wasUpdating){
46477                 layout.endUpdate();
46478             }
46479             this.state = state; 
46480         }
46481         this.layout = layout;
46482         layout.on("regionresized", this.onRegionResized, this);
46483         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46484         layout.on("regionexpanded", this.onRegionExpanded, this);
46485     },
46486     
46487     storeState : function(){
46488         this.provider.set(this.layout.id+"-layout-state", this.state);
46489     },
46490     
46491     onRegionResized : function(region, newSize){
46492         this.state[region.getPosition()].size = newSize;
46493         this.storeState();
46494     },
46495     
46496     onRegionCollapsed : function(region){
46497         this.state[region.getPosition()].collapsed = true;
46498         this.storeState();
46499     },
46500     
46501     onRegionExpanded : function(region){
46502         this.state[region.getPosition()].collapsed = false;
46503         this.storeState();
46504     }
46505 };/*
46506  * Based on:
46507  * Ext JS Library 1.1.1
46508  * Copyright(c) 2006-2007, Ext JS, LLC.
46509  *
46510  * Originally Released Under LGPL - original licence link has changed is not relivant.
46511  *
46512  * Fork - LGPL
46513  * <script type="text/javascript">
46514  */
46515 /**
46516  * @class Roo.ContentPanel
46517  * @extends Roo.util.Observable
46518  * A basic ContentPanel element.
46519  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46520  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46521  * @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
46522  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46523  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46524  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46525  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46526  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46527  * @cfg {String} title          The title for this panel
46528  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46529  * @cfg {String} url            Calls {@link #setUrl} with this value
46530  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46531  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46532  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46533  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46534
46535  * @constructor
46536  * Create a new ContentPanel.
46537  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46538  * @param {String/Object} config A string to set only the title or a config object
46539  * @param {String} content (optional) Set the HTML content for this panel
46540  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46541  */
46542 Roo.ContentPanel = function(el, config, content){
46543     
46544      
46545     /*
46546     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46547         config = el;
46548         el = Roo.id();
46549     }
46550     if (config && config.parentLayout) { 
46551         el = config.parentLayout.el.createChild(); 
46552     }
46553     */
46554     if(el.autoCreate){ // xtype is available if this is called from factory
46555         config = el;
46556         el = Roo.id();
46557     }
46558     this.el = Roo.get(el);
46559     if(!this.el && config && config.autoCreate){
46560         if(typeof config.autoCreate == "object"){
46561             if(!config.autoCreate.id){
46562                 config.autoCreate.id = config.id||el;
46563             }
46564             this.el = Roo.DomHelper.append(document.body,
46565                         config.autoCreate, true);
46566         }else{
46567             this.el = Roo.DomHelper.append(document.body,
46568                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46569         }
46570     }
46571     this.closable = false;
46572     this.loaded = false;
46573     this.active = false;
46574     if(typeof config == "string"){
46575         this.title = config;
46576     }else{
46577         Roo.apply(this, config);
46578     }
46579     
46580     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46581         this.wrapEl = this.el.wrap();
46582         this.toolbar.container = this.el.insertSibling(false, 'before');
46583         this.toolbar = new Roo.Toolbar(this.toolbar);
46584     }
46585     
46586     
46587     
46588     if(this.resizeEl){
46589         this.resizeEl = Roo.get(this.resizeEl, true);
46590     }else{
46591         this.resizeEl = this.el;
46592     }
46593     this.addEvents({
46594         /**
46595          * @event activate
46596          * Fires when this panel is activated. 
46597          * @param {Roo.ContentPanel} this
46598          */
46599         "activate" : true,
46600         /**
46601          * @event deactivate
46602          * Fires when this panel is activated. 
46603          * @param {Roo.ContentPanel} this
46604          */
46605         "deactivate" : true,
46606
46607         /**
46608          * @event resize
46609          * Fires when this panel is resized if fitToFrame is true.
46610          * @param {Roo.ContentPanel} this
46611          * @param {Number} width The width after any component adjustments
46612          * @param {Number} height The height after any component adjustments
46613          */
46614         "resize" : true,
46615         
46616          /**
46617          * @event render
46618          * Fires when this tab is created
46619          * @param {Roo.ContentPanel} this
46620          */
46621         "render" : true
46622         
46623         
46624         
46625     });
46626     if(this.autoScroll){
46627         this.resizeEl.setStyle("overflow", "auto");
46628     } else {
46629         // fix randome scrolling
46630         this.el.on('scroll', function() {
46631             Roo.log('fix random scolling');
46632             this.scrollTo('top',0); 
46633         });
46634     }
46635     content = content || this.content;
46636     if(content){
46637         this.setContent(content);
46638     }
46639     if(config && config.url){
46640         this.setUrl(this.url, this.params, this.loadOnce);
46641     }
46642     
46643     
46644     
46645     Roo.ContentPanel.superclass.constructor.call(this);
46646     
46647     this.fireEvent('render', this);
46648 };
46649
46650 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46651     tabTip:'',
46652     setRegion : function(region){
46653         this.region = region;
46654         if(region){
46655            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46656         }else{
46657            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46658         } 
46659     },
46660     
46661     /**
46662      * Returns the toolbar for this Panel if one was configured. 
46663      * @return {Roo.Toolbar} 
46664      */
46665     getToolbar : function(){
46666         return this.toolbar;
46667     },
46668     
46669     setActiveState : function(active){
46670         this.active = active;
46671         if(!active){
46672             this.fireEvent("deactivate", this);
46673         }else{
46674             this.fireEvent("activate", this);
46675         }
46676     },
46677     /**
46678      * Updates this panel's element
46679      * @param {String} content The new content
46680      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46681     */
46682     setContent : function(content, loadScripts){
46683         this.el.update(content, loadScripts);
46684     },
46685
46686     ignoreResize : function(w, h){
46687         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46688             return true;
46689         }else{
46690             this.lastSize = {width: w, height: h};
46691             return false;
46692         }
46693     },
46694     /**
46695      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46696      * @return {Roo.UpdateManager} The UpdateManager
46697      */
46698     getUpdateManager : function(){
46699         return this.el.getUpdateManager();
46700     },
46701      /**
46702      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46703      * @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:
46704 <pre><code>
46705 panel.load({
46706     url: "your-url.php",
46707     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46708     callback: yourFunction,
46709     scope: yourObject, //(optional scope)
46710     discardUrl: false,
46711     nocache: false,
46712     text: "Loading...",
46713     timeout: 30,
46714     scripts: false
46715 });
46716 </code></pre>
46717      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46718      * 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.
46719      * @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}
46720      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46721      * @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.
46722      * @return {Roo.ContentPanel} this
46723      */
46724     load : function(){
46725         var um = this.el.getUpdateManager();
46726         um.update.apply(um, arguments);
46727         return this;
46728     },
46729
46730
46731     /**
46732      * 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.
46733      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46734      * @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)
46735      * @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)
46736      * @return {Roo.UpdateManager} The UpdateManager
46737      */
46738     setUrl : function(url, params, loadOnce){
46739         if(this.refreshDelegate){
46740             this.removeListener("activate", this.refreshDelegate);
46741         }
46742         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46743         this.on("activate", this.refreshDelegate);
46744         return this.el.getUpdateManager();
46745     },
46746     
46747     _handleRefresh : function(url, params, loadOnce){
46748         if(!loadOnce || !this.loaded){
46749             var updater = this.el.getUpdateManager();
46750             updater.update(url, params, this._setLoaded.createDelegate(this));
46751         }
46752     },
46753     
46754     _setLoaded : function(){
46755         this.loaded = true;
46756     }, 
46757     
46758     /**
46759      * Returns this panel's id
46760      * @return {String} 
46761      */
46762     getId : function(){
46763         return this.el.id;
46764     },
46765     
46766     /** 
46767      * Returns this panel's element - used by regiosn to add.
46768      * @return {Roo.Element} 
46769      */
46770     getEl : function(){
46771         return this.wrapEl || this.el;
46772     },
46773     
46774     adjustForComponents : function(width, height){
46775         if(this.resizeEl != this.el){
46776             width -= this.el.getFrameWidth('lr');
46777             height -= this.el.getFrameWidth('tb');
46778         }
46779         if(this.toolbar){
46780             var te = this.toolbar.getEl();
46781             height -= te.getHeight();
46782             te.setWidth(width);
46783         }
46784         if(this.adjustments){
46785             width += this.adjustments[0];
46786             height += this.adjustments[1];
46787         }
46788         return {"width": width, "height": height};
46789     },
46790     
46791     setSize : function(width, height){
46792         if(this.fitToFrame && !this.ignoreResize(width, height)){
46793             if(this.fitContainer && this.resizeEl != this.el){
46794                 this.el.setSize(width, height);
46795             }
46796             var size = this.adjustForComponents(width, height);
46797             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46798             this.fireEvent('resize', this, size.width, size.height);
46799         }
46800     },
46801     
46802     /**
46803      * Returns this panel's title
46804      * @return {String} 
46805      */
46806     getTitle : function(){
46807         return this.title;
46808     },
46809     
46810     /**
46811      * Set this panel's title
46812      * @param {String} title
46813      */
46814     setTitle : function(title){
46815         this.title = title;
46816         if(this.region){
46817             this.region.updatePanelTitle(this, title);
46818         }
46819     },
46820     
46821     /**
46822      * Returns true is this panel was configured to be closable
46823      * @return {Boolean} 
46824      */
46825     isClosable : function(){
46826         return this.closable;
46827     },
46828     
46829     beforeSlide : function(){
46830         this.el.clip();
46831         this.resizeEl.clip();
46832     },
46833     
46834     afterSlide : function(){
46835         this.el.unclip();
46836         this.resizeEl.unclip();
46837     },
46838     
46839     /**
46840      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46841      *   Will fail silently if the {@link #setUrl} method has not been called.
46842      *   This does not activate the panel, just updates its content.
46843      */
46844     refresh : function(){
46845         if(this.refreshDelegate){
46846            this.loaded = false;
46847            this.refreshDelegate();
46848         }
46849     },
46850     
46851     /**
46852      * Destroys this panel
46853      */
46854     destroy : function(){
46855         this.el.removeAllListeners();
46856         var tempEl = document.createElement("span");
46857         tempEl.appendChild(this.el.dom);
46858         tempEl.innerHTML = "";
46859         this.el.remove();
46860         this.el = null;
46861     },
46862     
46863     /**
46864      * form - if the content panel contains a form - this is a reference to it.
46865      * @type {Roo.form.Form}
46866      */
46867     form : false,
46868     /**
46869      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46870      *    This contains a reference to it.
46871      * @type {Roo.View}
46872      */
46873     view : false,
46874     
46875       /**
46876      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46877      * <pre><code>
46878
46879 layout.addxtype({
46880        xtype : 'Form',
46881        items: [ .... ]
46882    }
46883 );
46884
46885 </code></pre>
46886      * @param {Object} cfg Xtype definition of item to add.
46887      */
46888     
46889     addxtype : function(cfg) {
46890         // add form..
46891         if (cfg.xtype.match(/^Form$/)) {
46892             var el = this.el.createChild();
46893
46894             this.form = new  Roo.form.Form(cfg);
46895             
46896             
46897             if ( this.form.allItems.length) this.form.render(el.dom);
46898             return this.form;
46899         }
46900         // should only have one of theses..
46901         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46902             // views..
46903             cfg.el = this.el.appendChild(document.createElement("div"));
46904             // factory?
46905             
46906             var ret = new Roo.factory(cfg);
46907             ret.render && ret.render(false, ''); // render blank..
46908             this.view = ret;
46909             return ret;
46910         }
46911         return false;
46912     }
46913 });
46914
46915 /**
46916  * @class Roo.GridPanel
46917  * @extends Roo.ContentPanel
46918  * @constructor
46919  * Create a new GridPanel.
46920  * @param {Roo.grid.Grid} grid The grid for this panel
46921  * @param {String/Object} config A string to set only the panel's title, or a config object
46922  */
46923 Roo.GridPanel = function(grid, config){
46924     
46925   
46926     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46927         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46928         
46929     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46930     
46931     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46932     
46933     if(this.toolbar){
46934         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46935     }
46936     // xtype created footer. - not sure if will work as we normally have to render first..
46937     if (this.footer && !this.footer.el && this.footer.xtype) {
46938         
46939         this.footer.container = this.grid.getView().getFooterPanel(true);
46940         this.footer.dataSource = this.grid.dataSource;
46941         this.footer = Roo.factory(this.footer, Roo);
46942         
46943     }
46944     
46945     grid.monitorWindowResize = false; // turn off autosizing
46946     grid.autoHeight = false;
46947     grid.autoWidth = false;
46948     this.grid = grid;
46949     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46950 };
46951
46952 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46953     getId : function(){
46954         return this.grid.id;
46955     },
46956     
46957     /**
46958      * Returns the grid for this panel
46959      * @return {Roo.grid.Grid} 
46960      */
46961     getGrid : function(){
46962         return this.grid;    
46963     },
46964     
46965     setSize : function(width, height){
46966         if(!this.ignoreResize(width, height)){
46967             var grid = this.grid;
46968             var size = this.adjustForComponents(width, height);
46969             grid.getGridEl().setSize(size.width, size.height);
46970             grid.autoSize();
46971         }
46972     },
46973     
46974     beforeSlide : function(){
46975         this.grid.getView().scroller.clip();
46976     },
46977     
46978     afterSlide : function(){
46979         this.grid.getView().scroller.unclip();
46980     },
46981     
46982     destroy : function(){
46983         this.grid.destroy();
46984         delete this.grid;
46985         Roo.GridPanel.superclass.destroy.call(this); 
46986     }
46987 });
46988
46989
46990 /**
46991  * @class Roo.NestedLayoutPanel
46992  * @extends Roo.ContentPanel
46993  * @constructor
46994  * Create a new NestedLayoutPanel.
46995  * 
46996  * 
46997  * @param {Roo.BorderLayout} layout The layout for this panel
46998  * @param {String/Object} config A string to set only the title or a config object
46999  */
47000 Roo.NestedLayoutPanel = function(layout, config)
47001 {
47002     // construct with only one argument..
47003     /* FIXME - implement nicer consturctors
47004     if (layout.layout) {
47005         config = layout;
47006         layout = config.layout;
47007         delete config.layout;
47008     }
47009     if (layout.xtype && !layout.getEl) {
47010         // then layout needs constructing..
47011         layout = Roo.factory(layout, Roo);
47012     }
47013     */
47014     
47015     
47016     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47017     
47018     layout.monitorWindowResize = false; // turn off autosizing
47019     this.layout = layout;
47020     this.layout.getEl().addClass("x-layout-nested-layout");
47021     
47022     
47023     
47024     
47025 };
47026
47027 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47028
47029     setSize : function(width, height){
47030         if(!this.ignoreResize(width, height)){
47031             var size = this.adjustForComponents(width, height);
47032             var el = this.layout.getEl();
47033             el.setSize(size.width, size.height);
47034             var touch = el.dom.offsetWidth;
47035             this.layout.layout();
47036             // ie requires a double layout on the first pass
47037             if(Roo.isIE && !this.initialized){
47038                 this.initialized = true;
47039                 this.layout.layout();
47040             }
47041         }
47042     },
47043     
47044     // activate all subpanels if not currently active..
47045     
47046     setActiveState : function(active){
47047         this.active = active;
47048         if(!active){
47049             this.fireEvent("deactivate", this);
47050             return;
47051         }
47052         
47053         this.fireEvent("activate", this);
47054         // not sure if this should happen before or after..
47055         if (!this.layout) {
47056             return; // should not happen..
47057         }
47058         var reg = false;
47059         for (var r in this.layout.regions) {
47060             reg = this.layout.getRegion(r);
47061             if (reg.getActivePanel()) {
47062                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47063                 reg.setActivePanel(reg.getActivePanel());
47064                 continue;
47065             }
47066             if (!reg.panels.length) {
47067                 continue;
47068             }
47069             reg.showPanel(reg.getPanel(0));
47070         }
47071         
47072         
47073         
47074         
47075     },
47076     
47077     /**
47078      * Returns the nested BorderLayout for this panel
47079      * @return {Roo.BorderLayout} 
47080      */
47081     getLayout : function(){
47082         return this.layout;
47083     },
47084     
47085      /**
47086      * Adds a xtype elements to the layout of the nested panel
47087      * <pre><code>
47088
47089 panel.addxtype({
47090        xtype : 'ContentPanel',
47091        region: 'west',
47092        items: [ .... ]
47093    }
47094 );
47095
47096 panel.addxtype({
47097         xtype : 'NestedLayoutPanel',
47098         region: 'west',
47099         layout: {
47100            center: { },
47101            west: { }   
47102         },
47103         items : [ ... list of content panels or nested layout panels.. ]
47104    }
47105 );
47106 </code></pre>
47107      * @param {Object} cfg Xtype definition of item to add.
47108      */
47109     addxtype : function(cfg) {
47110         return this.layout.addxtype(cfg);
47111     
47112     }
47113 });
47114
47115 Roo.ScrollPanel = function(el, config, content){
47116     config = config || {};
47117     config.fitToFrame = true;
47118     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47119     
47120     this.el.dom.style.overflow = "hidden";
47121     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47122     this.el.removeClass("x-layout-inactive-content");
47123     this.el.on("mousewheel", this.onWheel, this);
47124
47125     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47126     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47127     up.unselectable(); down.unselectable();
47128     up.on("click", this.scrollUp, this);
47129     down.on("click", this.scrollDown, this);
47130     up.addClassOnOver("x-scroller-btn-over");
47131     down.addClassOnOver("x-scroller-btn-over");
47132     up.addClassOnClick("x-scroller-btn-click");
47133     down.addClassOnClick("x-scroller-btn-click");
47134     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47135
47136     this.resizeEl = this.el;
47137     this.el = wrap; this.up = up; this.down = down;
47138 };
47139
47140 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47141     increment : 100,
47142     wheelIncrement : 5,
47143     scrollUp : function(){
47144         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47145     },
47146
47147     scrollDown : function(){
47148         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47149     },
47150
47151     afterScroll : function(){
47152         var el = this.resizeEl;
47153         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47154         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47155         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47156     },
47157
47158     setSize : function(){
47159         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47160         this.afterScroll();
47161     },
47162
47163     onWheel : function(e){
47164         var d = e.getWheelDelta();
47165         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47166         this.afterScroll();
47167         e.stopEvent();
47168     },
47169
47170     setContent : function(content, loadScripts){
47171         this.resizeEl.update(content, loadScripts);
47172     }
47173
47174 });
47175
47176
47177
47178
47179
47180
47181
47182
47183
47184 /**
47185  * @class Roo.TreePanel
47186  * @extends Roo.ContentPanel
47187  * @constructor
47188  * Create a new TreePanel. - defaults to fit/scoll contents.
47189  * @param {String/Object} config A string to set only the panel's title, or a config object
47190  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47191  */
47192 Roo.TreePanel = function(config){
47193     var el = config.el;
47194     var tree = config.tree;
47195     delete config.tree; 
47196     delete config.el; // hopefull!
47197     
47198     // wrapper for IE7 strict & safari scroll issue
47199     
47200     var treeEl = el.createChild();
47201     config.resizeEl = treeEl;
47202     
47203     
47204     
47205     Roo.TreePanel.superclass.constructor.call(this, el, config);
47206  
47207  
47208     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47209     //console.log(tree);
47210     this.on('activate', function()
47211     {
47212         if (this.tree.rendered) {
47213             return;
47214         }
47215         //console.log('render tree');
47216         this.tree.render();
47217     });
47218     
47219     this.on('resize',  function (cp, w, h) {
47220             this.tree.innerCt.setWidth(w);
47221             this.tree.innerCt.setHeight(h);
47222             this.tree.innerCt.setStyle('overflow-y', 'auto');
47223     });
47224
47225         
47226     
47227 };
47228
47229 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47230     fitToFrame : true,
47231     autoScroll : true
47232 });
47233
47234
47235
47236
47237
47238
47239
47240
47241
47242
47243
47244 /*
47245  * Based on:
47246  * Ext JS Library 1.1.1
47247  * Copyright(c) 2006-2007, Ext JS, LLC.
47248  *
47249  * Originally Released Under LGPL - original licence link has changed is not relivant.
47250  *
47251  * Fork - LGPL
47252  * <script type="text/javascript">
47253  */
47254  
47255
47256 /**
47257  * @class Roo.ReaderLayout
47258  * @extends Roo.BorderLayout
47259  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47260  * center region containing two nested regions (a top one for a list view and one for item preview below),
47261  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47262  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47263  * expedites the setup of the overall layout and regions for this common application style.
47264  * Example:
47265  <pre><code>
47266 var reader = new Roo.ReaderLayout();
47267 var CP = Roo.ContentPanel;  // shortcut for adding
47268
47269 reader.beginUpdate();
47270 reader.add("north", new CP("north", "North"));
47271 reader.add("west", new CP("west", {title: "West"}));
47272 reader.add("east", new CP("east", {title: "East"}));
47273
47274 reader.regions.listView.add(new CP("listView", "List"));
47275 reader.regions.preview.add(new CP("preview", "Preview"));
47276 reader.endUpdate();
47277 </code></pre>
47278 * @constructor
47279 * Create a new ReaderLayout
47280 * @param {Object} config Configuration options
47281 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47282 * document.body if omitted)
47283 */
47284 Roo.ReaderLayout = function(config, renderTo){
47285     var c = config || {size:{}};
47286     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47287         north: c.north !== false ? Roo.apply({
47288             split:false,
47289             initialSize: 32,
47290             titlebar: false
47291         }, c.north) : false,
47292         west: c.west !== false ? Roo.apply({
47293             split:true,
47294             initialSize: 200,
47295             minSize: 175,
47296             maxSize: 400,
47297             titlebar: true,
47298             collapsible: true,
47299             animate: true,
47300             margins:{left:5,right:0,bottom:5,top:5},
47301             cmargins:{left:5,right:5,bottom:5,top:5}
47302         }, c.west) : false,
47303         east: c.east !== false ? Roo.apply({
47304             split:true,
47305             initialSize: 200,
47306             minSize: 175,
47307             maxSize: 400,
47308             titlebar: true,
47309             collapsible: true,
47310             animate: true,
47311             margins:{left:0,right:5,bottom:5,top:5},
47312             cmargins:{left:5,right:5,bottom:5,top:5}
47313         }, c.east) : false,
47314         center: Roo.apply({
47315             tabPosition: 'top',
47316             autoScroll:false,
47317             closeOnTab: true,
47318             titlebar:false,
47319             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47320         }, c.center)
47321     });
47322
47323     this.el.addClass('x-reader');
47324
47325     this.beginUpdate();
47326
47327     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47328         south: c.preview !== false ? Roo.apply({
47329             split:true,
47330             initialSize: 200,
47331             minSize: 100,
47332             autoScroll:true,
47333             collapsible:true,
47334             titlebar: true,
47335             cmargins:{top:5,left:0, right:0, bottom:0}
47336         }, c.preview) : false,
47337         center: Roo.apply({
47338             autoScroll:false,
47339             titlebar:false,
47340             minHeight:200
47341         }, c.listView)
47342     });
47343     this.add('center', new Roo.NestedLayoutPanel(inner,
47344             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47345
47346     this.endUpdate();
47347
47348     this.regions.preview = inner.getRegion('south');
47349     this.regions.listView = inner.getRegion('center');
47350 };
47351
47352 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47353  * Based on:
47354  * Ext JS Library 1.1.1
47355  * Copyright(c) 2006-2007, Ext JS, LLC.
47356  *
47357  * Originally Released Under LGPL - original licence link has changed is not relivant.
47358  *
47359  * Fork - LGPL
47360  * <script type="text/javascript">
47361  */
47362  
47363 /**
47364  * @class Roo.grid.Grid
47365  * @extends Roo.util.Observable
47366  * This class represents the primary interface of a component based grid control.
47367  * <br><br>Usage:<pre><code>
47368  var grid = new Roo.grid.Grid("my-container-id", {
47369      ds: myDataStore,
47370      cm: myColModel,
47371      selModel: mySelectionModel,
47372      autoSizeColumns: true,
47373      monitorWindowResize: false,
47374      trackMouseOver: true
47375  });
47376  // set any options
47377  grid.render();
47378  * </code></pre>
47379  * <b>Common Problems:</b><br/>
47380  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47381  * element will correct this<br/>
47382  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47383  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47384  * are unpredictable.<br/>
47385  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47386  * grid to calculate dimensions/offsets.<br/>
47387   * @constructor
47388  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47389  * The container MUST have some type of size defined for the grid to fill. The container will be
47390  * automatically set to position relative if it isn't already.
47391  * @param {Object} config A config object that sets properties on this grid.
47392  */
47393 Roo.grid.Grid = function(container, config){
47394         // initialize the container
47395         this.container = Roo.get(container);
47396         this.container.update("");
47397         this.container.setStyle("overflow", "hidden");
47398     this.container.addClass('x-grid-container');
47399
47400     this.id = this.container.id;
47401
47402     Roo.apply(this, config);
47403     // check and correct shorthanded configs
47404     if(this.ds){
47405         this.dataSource = this.ds;
47406         delete this.ds;
47407     }
47408     if(this.cm){
47409         this.colModel = this.cm;
47410         delete this.cm;
47411     }
47412     if(this.sm){
47413         this.selModel = this.sm;
47414         delete this.sm;
47415     }
47416
47417     if (this.selModel) {
47418         this.selModel = Roo.factory(this.selModel, Roo.grid);
47419         this.sm = this.selModel;
47420         this.sm.xmodule = this.xmodule || false;
47421     }
47422     if (typeof(this.colModel.config) == 'undefined') {
47423         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47424         this.cm = this.colModel;
47425         this.cm.xmodule = this.xmodule || false;
47426     }
47427     if (this.dataSource) {
47428         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47429         this.ds = this.dataSource;
47430         this.ds.xmodule = this.xmodule || false;
47431          
47432     }
47433     
47434     
47435     
47436     if(this.width){
47437         this.container.setWidth(this.width);
47438     }
47439
47440     if(this.height){
47441         this.container.setHeight(this.height);
47442     }
47443     /** @private */
47444         this.addEvents({
47445         // raw events
47446         /**
47447          * @event click
47448          * The raw click event for the entire grid.
47449          * @param {Roo.EventObject} e
47450          */
47451         "click" : true,
47452         /**
47453          * @event dblclick
47454          * The raw dblclick event for the entire grid.
47455          * @param {Roo.EventObject} e
47456          */
47457         "dblclick" : true,
47458         /**
47459          * @event contextmenu
47460          * The raw contextmenu event for the entire grid.
47461          * @param {Roo.EventObject} e
47462          */
47463         "contextmenu" : true,
47464         /**
47465          * @event mousedown
47466          * The raw mousedown event for the entire grid.
47467          * @param {Roo.EventObject} e
47468          */
47469         "mousedown" : true,
47470         /**
47471          * @event mouseup
47472          * The raw mouseup event for the entire grid.
47473          * @param {Roo.EventObject} e
47474          */
47475         "mouseup" : true,
47476         /**
47477          * @event mouseover
47478          * The raw mouseover event for the entire grid.
47479          * @param {Roo.EventObject} e
47480          */
47481         "mouseover" : true,
47482         /**
47483          * @event mouseout
47484          * The raw mouseout event for the entire grid.
47485          * @param {Roo.EventObject} e
47486          */
47487         "mouseout" : true,
47488         /**
47489          * @event keypress
47490          * The raw keypress event for the entire grid.
47491          * @param {Roo.EventObject} e
47492          */
47493         "keypress" : true,
47494         /**
47495          * @event keydown
47496          * The raw keydown event for the entire grid.
47497          * @param {Roo.EventObject} e
47498          */
47499         "keydown" : true,
47500
47501         // custom events
47502
47503         /**
47504          * @event cellclick
47505          * Fires when a cell is clicked
47506          * @param {Grid} this
47507          * @param {Number} rowIndex
47508          * @param {Number} columnIndex
47509          * @param {Roo.EventObject} e
47510          */
47511         "cellclick" : true,
47512         /**
47513          * @event celldblclick
47514          * Fires when a cell is double clicked
47515          * @param {Grid} this
47516          * @param {Number} rowIndex
47517          * @param {Number} columnIndex
47518          * @param {Roo.EventObject} e
47519          */
47520         "celldblclick" : true,
47521         /**
47522          * @event rowclick
47523          * Fires when a row is clicked
47524          * @param {Grid} this
47525          * @param {Number} rowIndex
47526          * @param {Roo.EventObject} e
47527          */
47528         "rowclick" : true,
47529         /**
47530          * @event rowdblclick
47531          * Fires when a row is double clicked
47532          * @param {Grid} this
47533          * @param {Number} rowIndex
47534          * @param {Roo.EventObject} e
47535          */
47536         "rowdblclick" : true,
47537         /**
47538          * @event headerclick
47539          * Fires when a header is clicked
47540          * @param {Grid} this
47541          * @param {Number} columnIndex
47542          * @param {Roo.EventObject} e
47543          */
47544         "headerclick" : true,
47545         /**
47546          * @event headerdblclick
47547          * Fires when a header cell is double clicked
47548          * @param {Grid} this
47549          * @param {Number} columnIndex
47550          * @param {Roo.EventObject} e
47551          */
47552         "headerdblclick" : true,
47553         /**
47554          * @event rowcontextmenu
47555          * Fires when a row is right clicked
47556          * @param {Grid} this
47557          * @param {Number} rowIndex
47558          * @param {Roo.EventObject} e
47559          */
47560         "rowcontextmenu" : true,
47561         /**
47562          * @event cellcontextmenu
47563          * Fires when a cell is right clicked
47564          * @param {Grid} this
47565          * @param {Number} rowIndex
47566          * @param {Number} cellIndex
47567          * @param {Roo.EventObject} e
47568          */
47569          "cellcontextmenu" : true,
47570         /**
47571          * @event headercontextmenu
47572          * Fires when a header is right clicked
47573          * @param {Grid} this
47574          * @param {Number} columnIndex
47575          * @param {Roo.EventObject} e
47576          */
47577         "headercontextmenu" : true,
47578         /**
47579          * @event bodyscroll
47580          * Fires when the body element is scrolled
47581          * @param {Number} scrollLeft
47582          * @param {Number} scrollTop
47583          */
47584         "bodyscroll" : true,
47585         /**
47586          * @event columnresize
47587          * Fires when the user resizes a column
47588          * @param {Number} columnIndex
47589          * @param {Number} newSize
47590          */
47591         "columnresize" : true,
47592         /**
47593          * @event columnmove
47594          * Fires when the user moves a column
47595          * @param {Number} oldIndex
47596          * @param {Number} newIndex
47597          */
47598         "columnmove" : true,
47599         /**
47600          * @event startdrag
47601          * Fires when row(s) start being dragged
47602          * @param {Grid} this
47603          * @param {Roo.GridDD} dd The drag drop object
47604          * @param {event} e The raw browser event
47605          */
47606         "startdrag" : true,
47607         /**
47608          * @event enddrag
47609          * Fires when a drag operation is complete
47610          * @param {Grid} this
47611          * @param {Roo.GridDD} dd The drag drop object
47612          * @param {event} e The raw browser event
47613          */
47614         "enddrag" : true,
47615         /**
47616          * @event dragdrop
47617          * Fires when dragged row(s) are dropped on a valid DD target
47618          * @param {Grid} this
47619          * @param {Roo.GridDD} dd The drag drop object
47620          * @param {String} targetId The target drag drop object
47621          * @param {event} e The raw browser event
47622          */
47623         "dragdrop" : true,
47624         /**
47625          * @event dragover
47626          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47627          * @param {Grid} this
47628          * @param {Roo.GridDD} dd The drag drop object
47629          * @param {String} targetId The target drag drop object
47630          * @param {event} e The raw browser event
47631          */
47632         "dragover" : true,
47633         /**
47634          * @event dragenter
47635          *  Fires when the dragged row(s) first cross another DD target while being dragged
47636          * @param {Grid} this
47637          * @param {Roo.GridDD} dd The drag drop object
47638          * @param {String} targetId The target drag drop object
47639          * @param {event} e The raw browser event
47640          */
47641         "dragenter" : true,
47642         /**
47643          * @event dragout
47644          * Fires when the dragged row(s) leave another DD target while being dragged
47645          * @param {Grid} this
47646          * @param {Roo.GridDD} dd The drag drop object
47647          * @param {String} targetId The target drag drop object
47648          * @param {event} e The raw browser event
47649          */
47650         "dragout" : true,
47651         /**
47652          * @event rowclass
47653          * Fires when a row is rendered, so you can change add a style to it.
47654          * @param {GridView} gridview   The grid view
47655          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47656          */
47657         'rowclass' : true,
47658
47659         /**
47660          * @event render
47661          * Fires when the grid is rendered
47662          * @param {Grid} grid
47663          */
47664         'render' : true
47665     });
47666
47667     Roo.grid.Grid.superclass.constructor.call(this);
47668 };
47669 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47670     
47671     /**
47672      * @cfg {String} ddGroup - drag drop group.
47673      */
47674
47675     /**
47676      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47677      */
47678     minColumnWidth : 25,
47679
47680     /**
47681      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47682      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47683      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47684      */
47685     autoSizeColumns : false,
47686
47687     /**
47688      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47689      */
47690     autoSizeHeaders : true,
47691
47692     /**
47693      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47694      */
47695     monitorWindowResize : true,
47696
47697     /**
47698      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47699      * rows measured to get a columns size. Default is 0 (all rows).
47700      */
47701     maxRowsToMeasure : 0,
47702
47703     /**
47704      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47705      */
47706     trackMouseOver : true,
47707
47708     /**
47709     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47710     */
47711     
47712     /**
47713     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47714     */
47715     enableDragDrop : false,
47716     
47717     /**
47718     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47719     */
47720     enableColumnMove : true,
47721     
47722     /**
47723     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47724     */
47725     enableColumnHide : true,
47726     
47727     /**
47728     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47729     */
47730     enableRowHeightSync : false,
47731     
47732     /**
47733     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47734     */
47735     stripeRows : true,
47736     
47737     /**
47738     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47739     */
47740     autoHeight : false,
47741
47742     /**
47743      * @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.
47744      */
47745     autoExpandColumn : false,
47746
47747     /**
47748     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47749     * Default is 50.
47750     */
47751     autoExpandMin : 50,
47752
47753     /**
47754     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47755     */
47756     autoExpandMax : 1000,
47757
47758     /**
47759     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47760     */
47761     view : null,
47762
47763     /**
47764     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47765     */
47766     loadMask : false,
47767     /**
47768     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47769     */
47770     dropTarget: false,
47771     
47772    
47773     
47774     // private
47775     rendered : false,
47776
47777     /**
47778     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47779     * of a fixed width. Default is false.
47780     */
47781     /**
47782     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47783     */
47784     /**
47785      * Called once after all setup has been completed and the grid is ready to be rendered.
47786      * @return {Roo.grid.Grid} this
47787      */
47788     render : function()
47789     {
47790         var c = this.container;
47791         // try to detect autoHeight/width mode
47792         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47793             this.autoHeight = true;
47794         }
47795         var view = this.getView();
47796         view.init(this);
47797
47798         c.on("click", this.onClick, this);
47799         c.on("dblclick", this.onDblClick, this);
47800         c.on("contextmenu", this.onContextMenu, this);
47801         c.on("keydown", this.onKeyDown, this);
47802
47803         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47804
47805         this.getSelectionModel().init(this);
47806
47807         view.render();
47808
47809         if(this.loadMask){
47810             this.loadMask = new Roo.LoadMask(this.container,
47811                     Roo.apply({store:this.dataSource}, this.loadMask));
47812         }
47813         
47814         
47815         if (this.toolbar && this.toolbar.xtype) {
47816             this.toolbar.container = this.getView().getHeaderPanel(true);
47817             this.toolbar = new Roo.Toolbar(this.toolbar);
47818         }
47819         if (this.footer && this.footer.xtype) {
47820             this.footer.dataSource = this.getDataSource();
47821             this.footer.container = this.getView().getFooterPanel(true);
47822             this.footer = Roo.factory(this.footer, Roo);
47823         }
47824         if (this.dropTarget && this.dropTarget.xtype) {
47825             delete this.dropTarget.xtype;
47826             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47827         }
47828         
47829         
47830         this.rendered = true;
47831         this.fireEvent('render', this);
47832         return this;
47833     },
47834
47835         /**
47836          * Reconfigures the grid to use a different Store and Column Model.
47837          * The View will be bound to the new objects and refreshed.
47838          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47839          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47840          */
47841     reconfigure : function(dataSource, colModel){
47842         if(this.loadMask){
47843             this.loadMask.destroy();
47844             this.loadMask = new Roo.LoadMask(this.container,
47845                     Roo.apply({store:dataSource}, this.loadMask));
47846         }
47847         this.view.bind(dataSource, colModel);
47848         this.dataSource = dataSource;
47849         this.colModel = colModel;
47850         this.view.refresh(true);
47851     },
47852
47853     // private
47854     onKeyDown : function(e){
47855         this.fireEvent("keydown", e);
47856     },
47857
47858     /**
47859      * Destroy this grid.
47860      * @param {Boolean} removeEl True to remove the element
47861      */
47862     destroy : function(removeEl, keepListeners){
47863         if(this.loadMask){
47864             this.loadMask.destroy();
47865         }
47866         var c = this.container;
47867         c.removeAllListeners();
47868         this.view.destroy();
47869         this.colModel.purgeListeners();
47870         if(!keepListeners){
47871             this.purgeListeners();
47872         }
47873         c.update("");
47874         if(removeEl === true){
47875             c.remove();
47876         }
47877     },
47878
47879     // private
47880     processEvent : function(name, e){
47881         this.fireEvent(name, e);
47882         var t = e.getTarget();
47883         var v = this.view;
47884         var header = v.findHeaderIndex(t);
47885         if(header !== false){
47886             this.fireEvent("header" + name, this, header, e);
47887         }else{
47888             var row = v.findRowIndex(t);
47889             var cell = v.findCellIndex(t);
47890             if(row !== false){
47891                 this.fireEvent("row" + name, this, row, e);
47892                 if(cell !== false){
47893                     this.fireEvent("cell" + name, this, row, cell, e);
47894                 }
47895             }
47896         }
47897     },
47898
47899     // private
47900     onClick : function(e){
47901         this.processEvent("click", e);
47902     },
47903
47904     // private
47905     onContextMenu : function(e, t){
47906         this.processEvent("contextmenu", e);
47907     },
47908
47909     // private
47910     onDblClick : function(e){
47911         this.processEvent("dblclick", e);
47912     },
47913
47914     // private
47915     walkCells : function(row, col, step, fn, scope){
47916         var cm = this.colModel, clen = cm.getColumnCount();
47917         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47918         if(step < 0){
47919             if(col < 0){
47920                 row--;
47921                 first = false;
47922             }
47923             while(row >= 0){
47924                 if(!first){
47925                     col = clen-1;
47926                 }
47927                 first = false;
47928                 while(col >= 0){
47929                     if(fn.call(scope || this, row, col, cm) === true){
47930                         return [row, col];
47931                     }
47932                     col--;
47933                 }
47934                 row--;
47935             }
47936         } else {
47937             if(col >= clen){
47938                 row++;
47939                 first = false;
47940             }
47941             while(row < rlen){
47942                 if(!first){
47943                     col = 0;
47944                 }
47945                 first = false;
47946                 while(col < clen){
47947                     if(fn.call(scope || this, row, col, cm) === true){
47948                         return [row, col];
47949                     }
47950                     col++;
47951                 }
47952                 row++;
47953             }
47954         }
47955         return null;
47956     },
47957
47958     // private
47959     getSelections : function(){
47960         return this.selModel.getSelections();
47961     },
47962
47963     /**
47964      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47965      * but if manual update is required this method will initiate it.
47966      */
47967     autoSize : function(){
47968         if(this.rendered){
47969             this.view.layout();
47970             if(this.view.adjustForScroll){
47971                 this.view.adjustForScroll();
47972             }
47973         }
47974     },
47975
47976     /**
47977      * Returns the grid's underlying element.
47978      * @return {Element} The element
47979      */
47980     getGridEl : function(){
47981         return this.container;
47982     },
47983
47984     // private for compatibility, overridden by editor grid
47985     stopEditing : function(){},
47986
47987     /**
47988      * Returns the grid's SelectionModel.
47989      * @return {SelectionModel}
47990      */
47991     getSelectionModel : function(){
47992         if(!this.selModel){
47993             this.selModel = new Roo.grid.RowSelectionModel();
47994         }
47995         return this.selModel;
47996     },
47997
47998     /**
47999      * Returns the grid's DataSource.
48000      * @return {DataSource}
48001      */
48002     getDataSource : function(){
48003         return this.dataSource;
48004     },
48005
48006     /**
48007      * Returns the grid's ColumnModel.
48008      * @return {ColumnModel}
48009      */
48010     getColumnModel : function(){
48011         return this.colModel;
48012     },
48013
48014     /**
48015      * Returns the grid's GridView object.
48016      * @return {GridView}
48017      */
48018     getView : function(){
48019         if(!this.view){
48020             this.view = new Roo.grid.GridView(this.viewConfig);
48021         }
48022         return this.view;
48023     },
48024     /**
48025      * Called to get grid's drag proxy text, by default returns this.ddText.
48026      * @return {String}
48027      */
48028     getDragDropText : function(){
48029         var count = this.selModel.getCount();
48030         return String.format(this.ddText, count, count == 1 ? '' : 's');
48031     }
48032 });
48033 /**
48034  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48035  * %0 is replaced with the number of selected rows.
48036  * @type String
48037  */
48038 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48039  * Based on:
48040  * Ext JS Library 1.1.1
48041  * Copyright(c) 2006-2007, Ext JS, LLC.
48042  *
48043  * Originally Released Under LGPL - original licence link has changed is not relivant.
48044  *
48045  * Fork - LGPL
48046  * <script type="text/javascript">
48047  */
48048  
48049 Roo.grid.AbstractGridView = function(){
48050         this.grid = null;
48051         
48052         this.events = {
48053             "beforerowremoved" : true,
48054             "beforerowsinserted" : true,
48055             "beforerefresh" : true,
48056             "rowremoved" : true,
48057             "rowsinserted" : true,
48058             "rowupdated" : true,
48059             "refresh" : true
48060         };
48061     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48062 };
48063
48064 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48065     rowClass : "x-grid-row",
48066     cellClass : "x-grid-cell",
48067     tdClass : "x-grid-td",
48068     hdClass : "x-grid-hd",
48069     splitClass : "x-grid-hd-split",
48070     
48071         init: function(grid){
48072         this.grid = grid;
48073                 var cid = this.grid.getGridEl().id;
48074         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48075         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48076         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48077         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48078         },
48079         
48080         getColumnRenderers : function(){
48081         var renderers = [];
48082         var cm = this.grid.colModel;
48083         var colCount = cm.getColumnCount();
48084         for(var i = 0; i < colCount; i++){
48085             renderers[i] = cm.getRenderer(i);
48086         }
48087         return renderers;
48088     },
48089     
48090     getColumnIds : function(){
48091         var ids = [];
48092         var cm = this.grid.colModel;
48093         var colCount = cm.getColumnCount();
48094         for(var i = 0; i < colCount; i++){
48095             ids[i] = cm.getColumnId(i);
48096         }
48097         return ids;
48098     },
48099     
48100     getDataIndexes : function(){
48101         if(!this.indexMap){
48102             this.indexMap = this.buildIndexMap();
48103         }
48104         return this.indexMap.colToData;
48105     },
48106     
48107     getColumnIndexByDataIndex : function(dataIndex){
48108         if(!this.indexMap){
48109             this.indexMap = this.buildIndexMap();
48110         }
48111         return this.indexMap.dataToCol[dataIndex];
48112     },
48113     
48114     /**
48115      * Set a css style for a column dynamically. 
48116      * @param {Number} colIndex The index of the column
48117      * @param {String} name The css property name
48118      * @param {String} value The css value
48119      */
48120     setCSSStyle : function(colIndex, name, value){
48121         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48122         Roo.util.CSS.updateRule(selector, name, value);
48123     },
48124     
48125     generateRules : function(cm){
48126         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48127         Roo.util.CSS.removeStyleSheet(rulesId);
48128         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48129             var cid = cm.getColumnId(i);
48130             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48131                          this.tdSelector, cid, " {\n}\n",
48132                          this.hdSelector, cid, " {\n}\n",
48133                          this.splitSelector, cid, " {\n}\n");
48134         }
48135         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48136     }
48137 });/*
48138  * Based on:
48139  * Ext JS Library 1.1.1
48140  * Copyright(c) 2006-2007, Ext JS, LLC.
48141  *
48142  * Originally Released Under LGPL - original licence link has changed is not relivant.
48143  *
48144  * Fork - LGPL
48145  * <script type="text/javascript">
48146  */
48147
48148 // private
48149 // This is a support class used internally by the Grid components
48150 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48151     this.grid = grid;
48152     this.view = grid.getView();
48153     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48154     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48155     if(hd2){
48156         this.setHandleElId(Roo.id(hd));
48157         this.setOuterHandleElId(Roo.id(hd2));
48158     }
48159     this.scroll = false;
48160 };
48161 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48162     maxDragWidth: 120,
48163     getDragData : function(e){
48164         var t = Roo.lib.Event.getTarget(e);
48165         var h = this.view.findHeaderCell(t);
48166         if(h){
48167             return {ddel: h.firstChild, header:h};
48168         }
48169         return false;
48170     },
48171
48172     onInitDrag : function(e){
48173         this.view.headersDisabled = true;
48174         var clone = this.dragData.ddel.cloneNode(true);
48175         clone.id = Roo.id();
48176         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48177         this.proxy.update(clone);
48178         return true;
48179     },
48180
48181     afterValidDrop : function(){
48182         var v = this.view;
48183         setTimeout(function(){
48184             v.headersDisabled = false;
48185         }, 50);
48186     },
48187
48188     afterInvalidDrop : function(){
48189         var v = this.view;
48190         setTimeout(function(){
48191             v.headersDisabled = false;
48192         }, 50);
48193     }
48194 });
48195 /*
48196  * Based on:
48197  * Ext JS Library 1.1.1
48198  * Copyright(c) 2006-2007, Ext JS, LLC.
48199  *
48200  * Originally Released Under LGPL - original licence link has changed is not relivant.
48201  *
48202  * Fork - LGPL
48203  * <script type="text/javascript">
48204  */
48205 // private
48206 // This is a support class used internally by the Grid components
48207 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48208     this.grid = grid;
48209     this.view = grid.getView();
48210     // split the proxies so they don't interfere with mouse events
48211     this.proxyTop = Roo.DomHelper.append(document.body, {
48212         cls:"col-move-top", html:"&#160;"
48213     }, true);
48214     this.proxyBottom = Roo.DomHelper.append(document.body, {
48215         cls:"col-move-bottom", html:"&#160;"
48216     }, true);
48217     this.proxyTop.hide = this.proxyBottom.hide = function(){
48218         this.setLeftTop(-100,-100);
48219         this.setStyle("visibility", "hidden");
48220     };
48221     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48222     // temporarily disabled
48223     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48224     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48225 };
48226 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48227     proxyOffsets : [-4, -9],
48228     fly: Roo.Element.fly,
48229
48230     getTargetFromEvent : function(e){
48231         var t = Roo.lib.Event.getTarget(e);
48232         var cindex = this.view.findCellIndex(t);
48233         if(cindex !== false){
48234             return this.view.getHeaderCell(cindex);
48235         }
48236         return null;
48237     },
48238
48239     nextVisible : function(h){
48240         var v = this.view, cm = this.grid.colModel;
48241         h = h.nextSibling;
48242         while(h){
48243             if(!cm.isHidden(v.getCellIndex(h))){
48244                 return h;
48245             }
48246             h = h.nextSibling;
48247         }
48248         return null;
48249     },
48250
48251     prevVisible : function(h){
48252         var v = this.view, cm = this.grid.colModel;
48253         h = h.prevSibling;
48254         while(h){
48255             if(!cm.isHidden(v.getCellIndex(h))){
48256                 return h;
48257             }
48258             h = h.prevSibling;
48259         }
48260         return null;
48261     },
48262
48263     positionIndicator : function(h, n, e){
48264         var x = Roo.lib.Event.getPageX(e);
48265         var r = Roo.lib.Dom.getRegion(n.firstChild);
48266         var px, pt, py = r.top + this.proxyOffsets[1];
48267         if((r.right - x) <= (r.right-r.left)/2){
48268             px = r.right+this.view.borderWidth;
48269             pt = "after";
48270         }else{
48271             px = r.left;
48272             pt = "before";
48273         }
48274         var oldIndex = this.view.getCellIndex(h);
48275         var newIndex = this.view.getCellIndex(n);
48276
48277         if(this.grid.colModel.isFixed(newIndex)){
48278             return false;
48279         }
48280
48281         var locked = this.grid.colModel.isLocked(newIndex);
48282
48283         if(pt == "after"){
48284             newIndex++;
48285         }
48286         if(oldIndex < newIndex){
48287             newIndex--;
48288         }
48289         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48290             return false;
48291         }
48292         px +=  this.proxyOffsets[0];
48293         this.proxyTop.setLeftTop(px, py);
48294         this.proxyTop.show();
48295         if(!this.bottomOffset){
48296             this.bottomOffset = this.view.mainHd.getHeight();
48297         }
48298         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48299         this.proxyBottom.show();
48300         return pt;
48301     },
48302
48303     onNodeEnter : function(n, dd, e, data){
48304         if(data.header != n){
48305             this.positionIndicator(data.header, n, e);
48306         }
48307     },
48308
48309     onNodeOver : function(n, dd, e, data){
48310         var result = false;
48311         if(data.header != n){
48312             result = this.positionIndicator(data.header, n, e);
48313         }
48314         if(!result){
48315             this.proxyTop.hide();
48316             this.proxyBottom.hide();
48317         }
48318         return result ? this.dropAllowed : this.dropNotAllowed;
48319     },
48320
48321     onNodeOut : function(n, dd, e, data){
48322         this.proxyTop.hide();
48323         this.proxyBottom.hide();
48324     },
48325
48326     onNodeDrop : function(n, dd, e, data){
48327         var h = data.header;
48328         if(h != n){
48329             var cm = this.grid.colModel;
48330             var x = Roo.lib.Event.getPageX(e);
48331             var r = Roo.lib.Dom.getRegion(n.firstChild);
48332             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48333             var oldIndex = this.view.getCellIndex(h);
48334             var newIndex = this.view.getCellIndex(n);
48335             var locked = cm.isLocked(newIndex);
48336             if(pt == "after"){
48337                 newIndex++;
48338             }
48339             if(oldIndex < newIndex){
48340                 newIndex--;
48341             }
48342             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48343                 return false;
48344             }
48345             cm.setLocked(oldIndex, locked, true);
48346             cm.moveColumn(oldIndex, newIndex);
48347             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48348             return true;
48349         }
48350         return false;
48351     }
48352 });
48353 /*
48354  * Based on:
48355  * Ext JS Library 1.1.1
48356  * Copyright(c) 2006-2007, Ext JS, LLC.
48357  *
48358  * Originally Released Under LGPL - original licence link has changed is not relivant.
48359  *
48360  * Fork - LGPL
48361  * <script type="text/javascript">
48362  */
48363   
48364 /**
48365  * @class Roo.grid.GridView
48366  * @extends Roo.util.Observable
48367  *
48368  * @constructor
48369  * @param {Object} config
48370  */
48371 Roo.grid.GridView = function(config){
48372     Roo.grid.GridView.superclass.constructor.call(this);
48373     this.el = null;
48374
48375     Roo.apply(this, config);
48376 };
48377
48378 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48379
48380     /**
48381      * Override this function to apply custom css classes to rows during rendering
48382      * @param {Record} record The record
48383      * @param {Number} index
48384      * @method getRowClass
48385      */
48386     rowClass : "x-grid-row",
48387
48388     cellClass : "x-grid-col",
48389
48390     tdClass : "x-grid-td",
48391
48392     hdClass : "x-grid-hd",
48393
48394     splitClass : "x-grid-split",
48395
48396     sortClasses : ["sort-asc", "sort-desc"],
48397
48398     enableMoveAnim : false,
48399
48400     hlColor: "C3DAF9",
48401
48402     dh : Roo.DomHelper,
48403
48404     fly : Roo.Element.fly,
48405
48406     css : Roo.util.CSS,
48407
48408     borderWidth: 1,
48409
48410     splitOffset: 3,
48411
48412     scrollIncrement : 22,
48413
48414     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48415
48416     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48417
48418     bind : function(ds, cm){
48419         if(this.ds){
48420             this.ds.un("load", this.onLoad, this);
48421             this.ds.un("datachanged", this.onDataChange, this);
48422             this.ds.un("add", this.onAdd, this);
48423             this.ds.un("remove", this.onRemove, this);
48424             this.ds.un("update", this.onUpdate, this);
48425             this.ds.un("clear", this.onClear, this);
48426         }
48427         if(ds){
48428             ds.on("load", this.onLoad, this);
48429             ds.on("datachanged", this.onDataChange, this);
48430             ds.on("add", this.onAdd, this);
48431             ds.on("remove", this.onRemove, this);
48432             ds.on("update", this.onUpdate, this);
48433             ds.on("clear", this.onClear, this);
48434         }
48435         this.ds = ds;
48436
48437         if(this.cm){
48438             this.cm.un("widthchange", this.onColWidthChange, this);
48439             this.cm.un("headerchange", this.onHeaderChange, this);
48440             this.cm.un("hiddenchange", this.onHiddenChange, this);
48441             this.cm.un("columnmoved", this.onColumnMove, this);
48442             this.cm.un("columnlockchange", this.onColumnLock, this);
48443         }
48444         if(cm){
48445             this.generateRules(cm);
48446             cm.on("widthchange", this.onColWidthChange, this);
48447             cm.on("headerchange", this.onHeaderChange, this);
48448             cm.on("hiddenchange", this.onHiddenChange, this);
48449             cm.on("columnmoved", this.onColumnMove, this);
48450             cm.on("columnlockchange", this.onColumnLock, this);
48451         }
48452         this.cm = cm;
48453     },
48454
48455     init: function(grid){
48456         Roo.grid.GridView.superclass.init.call(this, grid);
48457
48458         this.bind(grid.dataSource, grid.colModel);
48459
48460         grid.on("headerclick", this.handleHeaderClick, this);
48461
48462         if(grid.trackMouseOver){
48463             grid.on("mouseover", this.onRowOver, this);
48464             grid.on("mouseout", this.onRowOut, this);
48465         }
48466         grid.cancelTextSelection = function(){};
48467         this.gridId = grid.id;
48468
48469         var tpls = this.templates || {};
48470
48471         if(!tpls.master){
48472             tpls.master = new Roo.Template(
48473                '<div class="x-grid" hidefocus="true">',
48474                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48475                   '<div class="x-grid-topbar"></div>',
48476                   '<div class="x-grid-scroller"><div></div></div>',
48477                   '<div class="x-grid-locked">',
48478                       '<div class="x-grid-header">{lockedHeader}</div>',
48479                       '<div class="x-grid-body">{lockedBody}</div>',
48480                   "</div>",
48481                   '<div class="x-grid-viewport">',
48482                       '<div class="x-grid-header">{header}</div>',
48483                       '<div class="x-grid-body">{body}</div>',
48484                   "</div>",
48485                   '<div class="x-grid-bottombar"></div>',
48486                  
48487                   '<div class="x-grid-resize-proxy">&#160;</div>',
48488                "</div>"
48489             );
48490             tpls.master.disableformats = true;
48491         }
48492
48493         if(!tpls.header){
48494             tpls.header = new Roo.Template(
48495                '<table border="0" cellspacing="0" cellpadding="0">',
48496                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48497                "</table>{splits}"
48498             );
48499             tpls.header.disableformats = true;
48500         }
48501         tpls.header.compile();
48502
48503         if(!tpls.hcell){
48504             tpls.hcell = new Roo.Template(
48505                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48506                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48507                 "</div></td>"
48508              );
48509              tpls.hcell.disableFormats = true;
48510         }
48511         tpls.hcell.compile();
48512
48513         if(!tpls.hsplit){
48514             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48515             tpls.hsplit.disableFormats = true;
48516         }
48517         tpls.hsplit.compile();
48518
48519         if(!tpls.body){
48520             tpls.body = new Roo.Template(
48521                '<table border="0" cellspacing="0" cellpadding="0">',
48522                "<tbody>{rows}</tbody>",
48523                "</table>"
48524             );
48525             tpls.body.disableFormats = true;
48526         }
48527         tpls.body.compile();
48528
48529         if(!tpls.row){
48530             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48531             tpls.row.disableFormats = true;
48532         }
48533         tpls.row.compile();
48534
48535         if(!tpls.cell){
48536             tpls.cell = new Roo.Template(
48537                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48538                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48539                 "</td>"
48540             );
48541             tpls.cell.disableFormats = true;
48542         }
48543         tpls.cell.compile();
48544
48545         this.templates = tpls;
48546     },
48547
48548     // remap these for backwards compat
48549     onColWidthChange : function(){
48550         this.updateColumns.apply(this, arguments);
48551     },
48552     onHeaderChange : function(){
48553         this.updateHeaders.apply(this, arguments);
48554     }, 
48555     onHiddenChange : function(){
48556         this.handleHiddenChange.apply(this, arguments);
48557     },
48558     onColumnMove : function(){
48559         this.handleColumnMove.apply(this, arguments);
48560     },
48561     onColumnLock : function(){
48562         this.handleLockChange.apply(this, arguments);
48563     },
48564
48565     onDataChange : function(){
48566         this.refresh();
48567         this.updateHeaderSortState();
48568     },
48569
48570     onClear : function(){
48571         this.refresh();
48572     },
48573
48574     onUpdate : function(ds, record){
48575         this.refreshRow(record);
48576     },
48577
48578     refreshRow : function(record){
48579         var ds = this.ds, index;
48580         if(typeof record == 'number'){
48581             index = record;
48582             record = ds.getAt(index);
48583         }else{
48584             index = ds.indexOf(record);
48585         }
48586         this.insertRows(ds, index, index, true);
48587         this.onRemove(ds, record, index+1, true);
48588         this.syncRowHeights(index, index);
48589         this.layout();
48590         this.fireEvent("rowupdated", this, index, record);
48591     },
48592
48593     onAdd : function(ds, records, index){
48594         this.insertRows(ds, index, index + (records.length-1));
48595     },
48596
48597     onRemove : function(ds, record, index, isUpdate){
48598         if(isUpdate !== true){
48599             this.fireEvent("beforerowremoved", this, index, record);
48600         }
48601         var bt = this.getBodyTable(), lt = this.getLockedTable();
48602         if(bt.rows[index]){
48603             bt.firstChild.removeChild(bt.rows[index]);
48604         }
48605         if(lt.rows[index]){
48606             lt.firstChild.removeChild(lt.rows[index]);
48607         }
48608         if(isUpdate !== true){
48609             this.stripeRows(index);
48610             this.syncRowHeights(index, index);
48611             this.layout();
48612             this.fireEvent("rowremoved", this, index, record);
48613         }
48614     },
48615
48616     onLoad : function(){
48617         this.scrollToTop();
48618     },
48619
48620     /**
48621      * Scrolls the grid to the top
48622      */
48623     scrollToTop : function(){
48624         if(this.scroller){
48625             this.scroller.dom.scrollTop = 0;
48626             this.syncScroll();
48627         }
48628     },
48629
48630     /**
48631      * Gets a panel in the header of the grid that can be used for toolbars etc.
48632      * After modifying the contents of this panel a call to grid.autoSize() may be
48633      * required to register any changes in size.
48634      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48635      * @return Roo.Element
48636      */
48637     getHeaderPanel : function(doShow){
48638         if(doShow){
48639             this.headerPanel.show();
48640         }
48641         return this.headerPanel;
48642     },
48643
48644     /**
48645      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48646      * After modifying the contents of this panel a call to grid.autoSize() may be
48647      * required to register any changes in size.
48648      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48649      * @return Roo.Element
48650      */
48651     getFooterPanel : function(doShow){
48652         if(doShow){
48653             this.footerPanel.show();
48654         }
48655         return this.footerPanel;
48656     },
48657
48658     initElements : function(){
48659         var E = Roo.Element;
48660         var el = this.grid.getGridEl().dom.firstChild;
48661         var cs = el.childNodes;
48662
48663         this.el = new E(el);
48664         
48665          this.focusEl = new E(el.firstChild);
48666         this.focusEl.swallowEvent("click", true);
48667         
48668         this.headerPanel = new E(cs[1]);
48669         this.headerPanel.enableDisplayMode("block");
48670
48671         this.scroller = new E(cs[2]);
48672         this.scrollSizer = new E(this.scroller.dom.firstChild);
48673
48674         this.lockedWrap = new E(cs[3]);
48675         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48676         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48677
48678         this.mainWrap = new E(cs[4]);
48679         this.mainHd = new E(this.mainWrap.dom.firstChild);
48680         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48681
48682         this.footerPanel = new E(cs[5]);
48683         this.footerPanel.enableDisplayMode("block");
48684
48685         this.resizeProxy = new E(cs[6]);
48686
48687         this.headerSelector = String.format(
48688            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48689            this.lockedHd.id, this.mainHd.id
48690         );
48691
48692         this.splitterSelector = String.format(
48693            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48694            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48695         );
48696     },
48697     idToCssName : function(s)
48698     {
48699         return s.replace(/[^a-z0-9]+/ig, '-');
48700     },
48701
48702     getHeaderCell : function(index){
48703         return Roo.DomQuery.select(this.headerSelector)[index];
48704     },
48705
48706     getHeaderCellMeasure : function(index){
48707         return this.getHeaderCell(index).firstChild;
48708     },
48709
48710     getHeaderCellText : function(index){
48711         return this.getHeaderCell(index).firstChild.firstChild;
48712     },
48713
48714     getLockedTable : function(){
48715         return this.lockedBody.dom.firstChild;
48716     },
48717
48718     getBodyTable : function(){
48719         return this.mainBody.dom.firstChild;
48720     },
48721
48722     getLockedRow : function(index){
48723         return this.getLockedTable().rows[index];
48724     },
48725
48726     getRow : function(index){
48727         return this.getBodyTable().rows[index];
48728     },
48729
48730     getRowComposite : function(index){
48731         if(!this.rowEl){
48732             this.rowEl = new Roo.CompositeElementLite();
48733         }
48734         var els = [], lrow, mrow;
48735         if(lrow = this.getLockedRow(index)){
48736             els.push(lrow);
48737         }
48738         if(mrow = this.getRow(index)){
48739             els.push(mrow);
48740         }
48741         this.rowEl.elements = els;
48742         return this.rowEl;
48743     },
48744     /**
48745      * Gets the 'td' of the cell
48746      * 
48747      * @param {Integer} rowIndex row to select
48748      * @param {Integer} colIndex column to select
48749      * 
48750      * @return {Object} 
48751      */
48752     getCell : function(rowIndex, colIndex){
48753         var locked = this.cm.getLockedCount();
48754         var source;
48755         if(colIndex < locked){
48756             source = this.lockedBody.dom.firstChild;
48757         }else{
48758             source = this.mainBody.dom.firstChild;
48759             colIndex -= locked;
48760         }
48761         return source.rows[rowIndex].childNodes[colIndex];
48762     },
48763
48764     getCellText : function(rowIndex, colIndex){
48765         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48766     },
48767
48768     getCellBox : function(cell){
48769         var b = this.fly(cell).getBox();
48770         if(Roo.isOpera){ // opera fails to report the Y
48771             b.y = cell.offsetTop + this.mainBody.getY();
48772         }
48773         return b;
48774     },
48775
48776     getCellIndex : function(cell){
48777         var id = String(cell.className).match(this.cellRE);
48778         if(id){
48779             return parseInt(id[1], 10);
48780         }
48781         return 0;
48782     },
48783
48784     findHeaderIndex : function(n){
48785         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48786         return r ? this.getCellIndex(r) : false;
48787     },
48788
48789     findHeaderCell : function(n){
48790         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48791         return r ? r : false;
48792     },
48793
48794     findRowIndex : function(n){
48795         if(!n){
48796             return false;
48797         }
48798         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48799         return r ? r.rowIndex : false;
48800     },
48801
48802     findCellIndex : function(node){
48803         var stop = this.el.dom;
48804         while(node && node != stop){
48805             if(this.findRE.test(node.className)){
48806                 return this.getCellIndex(node);
48807             }
48808             node = node.parentNode;
48809         }
48810         return false;
48811     },
48812
48813     getColumnId : function(index){
48814         return this.cm.getColumnId(index);
48815     },
48816
48817     getSplitters : function()
48818     {
48819         if(this.splitterSelector){
48820            return Roo.DomQuery.select(this.splitterSelector);
48821         }else{
48822             return null;
48823       }
48824     },
48825
48826     getSplitter : function(index){
48827         return this.getSplitters()[index];
48828     },
48829
48830     onRowOver : function(e, t){
48831         var row;
48832         if((row = this.findRowIndex(t)) !== false){
48833             this.getRowComposite(row).addClass("x-grid-row-over");
48834         }
48835     },
48836
48837     onRowOut : function(e, t){
48838         var row;
48839         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48840             this.getRowComposite(row).removeClass("x-grid-row-over");
48841         }
48842     },
48843
48844     renderHeaders : function(){
48845         var cm = this.cm;
48846         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48847         var cb = [], lb = [], sb = [], lsb = [], p = {};
48848         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48849             p.cellId = "x-grid-hd-0-" + i;
48850             p.splitId = "x-grid-csplit-0-" + i;
48851             p.id = cm.getColumnId(i);
48852             p.title = cm.getColumnTooltip(i) || "";
48853             p.value = cm.getColumnHeader(i) || "";
48854             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48855             if(!cm.isLocked(i)){
48856                 cb[cb.length] = ct.apply(p);
48857                 sb[sb.length] = st.apply(p);
48858             }else{
48859                 lb[lb.length] = ct.apply(p);
48860                 lsb[lsb.length] = st.apply(p);
48861             }
48862         }
48863         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48864                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48865     },
48866
48867     updateHeaders : function(){
48868         var html = this.renderHeaders();
48869         this.lockedHd.update(html[0]);
48870         this.mainHd.update(html[1]);
48871     },
48872
48873     /**
48874      * Focuses the specified row.
48875      * @param {Number} row The row index
48876      */
48877     focusRow : function(row)
48878     {
48879         //Roo.log('GridView.focusRow');
48880         var x = this.scroller.dom.scrollLeft;
48881         this.focusCell(row, 0, false);
48882         this.scroller.dom.scrollLeft = x;
48883     },
48884
48885     /**
48886      * Focuses the specified cell.
48887      * @param {Number} row The row index
48888      * @param {Number} col The column index
48889      * @param {Boolean} hscroll false to disable horizontal scrolling
48890      */
48891     focusCell : function(row, col, hscroll)
48892     {
48893         //Roo.log('GridView.focusCell');
48894         var el = this.ensureVisible(row, col, hscroll);
48895         this.focusEl.alignTo(el, "tl-tl");
48896         if(Roo.isGecko){
48897             this.focusEl.focus();
48898         }else{
48899             this.focusEl.focus.defer(1, this.focusEl);
48900         }
48901     },
48902
48903     /**
48904      * Scrolls the specified cell into view
48905      * @param {Number} row The row index
48906      * @param {Number} col The column index
48907      * @param {Boolean} hscroll false to disable horizontal scrolling
48908      */
48909     ensureVisible : function(row, col, hscroll)
48910     {
48911         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48912         //return null; //disable for testing.
48913         if(typeof row != "number"){
48914             row = row.rowIndex;
48915         }
48916         if(row < 0 && row >= this.ds.getCount()){
48917             return  null;
48918         }
48919         col = (col !== undefined ? col : 0);
48920         var cm = this.grid.colModel;
48921         while(cm.isHidden(col)){
48922             col++;
48923         }
48924
48925         var el = this.getCell(row, col);
48926         if(!el){
48927             return null;
48928         }
48929         var c = this.scroller.dom;
48930
48931         var ctop = parseInt(el.offsetTop, 10);
48932         var cleft = parseInt(el.offsetLeft, 10);
48933         var cbot = ctop + el.offsetHeight;
48934         var cright = cleft + el.offsetWidth;
48935         
48936         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48937         var stop = parseInt(c.scrollTop, 10);
48938         var sleft = parseInt(c.scrollLeft, 10);
48939         var sbot = stop + ch;
48940         var sright = sleft + c.clientWidth;
48941         /*
48942         Roo.log('GridView.ensureVisible:' +
48943                 ' ctop:' + ctop +
48944                 ' c.clientHeight:' + c.clientHeight +
48945                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48946                 ' stop:' + stop +
48947                 ' cbot:' + cbot +
48948                 ' sbot:' + sbot +
48949                 ' ch:' + ch  
48950                 );
48951         */
48952         if(ctop < stop){
48953              c.scrollTop = ctop;
48954             //Roo.log("set scrolltop to ctop DISABLE?");
48955         }else if(cbot > sbot){
48956             //Roo.log("set scrolltop to cbot-ch");
48957             c.scrollTop = cbot-ch;
48958         }
48959         
48960         if(hscroll !== false){
48961             if(cleft < sleft){
48962                 c.scrollLeft = cleft;
48963             }else if(cright > sright){
48964                 c.scrollLeft = cright-c.clientWidth;
48965             }
48966         }
48967          
48968         return el;
48969     },
48970
48971     updateColumns : function(){
48972         this.grid.stopEditing();
48973         var cm = this.grid.colModel, colIds = this.getColumnIds();
48974         //var totalWidth = cm.getTotalWidth();
48975         var pos = 0;
48976         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48977             //if(cm.isHidden(i)) continue;
48978             var w = cm.getColumnWidth(i);
48979             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48980             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48981         }
48982         this.updateSplitters();
48983     },
48984
48985     generateRules : function(cm){
48986         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48987         Roo.util.CSS.removeStyleSheet(rulesId);
48988         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48989             var cid = cm.getColumnId(i);
48990             var align = '';
48991             if(cm.config[i].align){
48992                 align = 'text-align:'+cm.config[i].align+';';
48993             }
48994             var hidden = '';
48995             if(cm.isHidden(i)){
48996                 hidden = 'display:none;';
48997             }
48998             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48999             ruleBuf.push(
49000                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49001                     this.hdSelector, cid, " {\n", align, width, "}\n",
49002                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49003                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49004         }
49005         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49006     },
49007
49008     updateSplitters : function(){
49009         var cm = this.cm, s = this.getSplitters();
49010         if(s){ // splitters not created yet
49011             var pos = 0, locked = true;
49012             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49013                 if(cm.isHidden(i)) continue;
49014                 var w = cm.getColumnWidth(i); // make sure it's a number
49015                 if(!cm.isLocked(i) && locked){
49016                     pos = 0;
49017                     locked = false;
49018                 }
49019                 pos += w;
49020                 s[i].style.left = (pos-this.splitOffset) + "px";
49021             }
49022         }
49023     },
49024
49025     handleHiddenChange : function(colModel, colIndex, hidden){
49026         if(hidden){
49027             this.hideColumn(colIndex);
49028         }else{
49029             this.unhideColumn(colIndex);
49030         }
49031     },
49032
49033     hideColumn : function(colIndex){
49034         var cid = this.getColumnId(colIndex);
49035         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49036         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49037         if(Roo.isSafari){
49038             this.updateHeaders();
49039         }
49040         this.updateSplitters();
49041         this.layout();
49042     },
49043
49044     unhideColumn : function(colIndex){
49045         var cid = this.getColumnId(colIndex);
49046         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49047         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49048
49049         if(Roo.isSafari){
49050             this.updateHeaders();
49051         }
49052         this.updateSplitters();
49053         this.layout();
49054     },
49055
49056     insertRows : function(dm, firstRow, lastRow, isUpdate){
49057         if(firstRow == 0 && lastRow == dm.getCount()-1){
49058             this.refresh();
49059         }else{
49060             if(!isUpdate){
49061                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49062             }
49063             var s = this.getScrollState();
49064             var markup = this.renderRows(firstRow, lastRow);
49065             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49066             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49067             this.restoreScroll(s);
49068             if(!isUpdate){
49069                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49070                 this.syncRowHeights(firstRow, lastRow);
49071                 this.stripeRows(firstRow);
49072                 this.layout();
49073             }
49074         }
49075     },
49076
49077     bufferRows : function(markup, target, index){
49078         var before = null, trows = target.rows, tbody = target.tBodies[0];
49079         if(index < trows.length){
49080             before = trows[index];
49081         }
49082         var b = document.createElement("div");
49083         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49084         var rows = b.firstChild.rows;
49085         for(var i = 0, len = rows.length; i < len; i++){
49086             if(before){
49087                 tbody.insertBefore(rows[0], before);
49088             }else{
49089                 tbody.appendChild(rows[0]);
49090             }
49091         }
49092         b.innerHTML = "";
49093         b = null;
49094     },
49095
49096     deleteRows : function(dm, firstRow, lastRow){
49097         if(dm.getRowCount()<1){
49098             this.fireEvent("beforerefresh", this);
49099             this.mainBody.update("");
49100             this.lockedBody.update("");
49101             this.fireEvent("refresh", this);
49102         }else{
49103             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49104             var bt = this.getBodyTable();
49105             var tbody = bt.firstChild;
49106             var rows = bt.rows;
49107             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49108                 tbody.removeChild(rows[firstRow]);
49109             }
49110             this.stripeRows(firstRow);
49111             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49112         }
49113     },
49114
49115     updateRows : function(dataSource, firstRow, lastRow){
49116         var s = this.getScrollState();
49117         this.refresh();
49118         this.restoreScroll(s);
49119     },
49120
49121     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49122         if(!noRefresh){
49123            this.refresh();
49124         }
49125         this.updateHeaderSortState();
49126     },
49127
49128     getScrollState : function(){
49129         
49130         var sb = this.scroller.dom;
49131         return {left: sb.scrollLeft, top: sb.scrollTop};
49132     },
49133
49134     stripeRows : function(startRow){
49135         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49136             return;
49137         }
49138         startRow = startRow || 0;
49139         var rows = this.getBodyTable().rows;
49140         var lrows = this.getLockedTable().rows;
49141         var cls = ' x-grid-row-alt ';
49142         for(var i = startRow, len = rows.length; i < len; i++){
49143             var row = rows[i], lrow = lrows[i];
49144             var isAlt = ((i+1) % 2 == 0);
49145             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49146             if(isAlt == hasAlt){
49147                 continue;
49148             }
49149             if(isAlt){
49150                 row.className += " x-grid-row-alt";
49151             }else{
49152                 row.className = row.className.replace("x-grid-row-alt", "");
49153             }
49154             if(lrow){
49155                 lrow.className = row.className;
49156             }
49157         }
49158     },
49159
49160     restoreScroll : function(state){
49161         //Roo.log('GridView.restoreScroll');
49162         var sb = this.scroller.dom;
49163         sb.scrollLeft = state.left;
49164         sb.scrollTop = state.top;
49165         this.syncScroll();
49166     },
49167
49168     syncScroll : function(){
49169         //Roo.log('GridView.syncScroll');
49170         var sb = this.scroller.dom;
49171         var sh = this.mainHd.dom;
49172         var bs = this.mainBody.dom;
49173         var lv = this.lockedBody.dom;
49174         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49175         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49176     },
49177
49178     handleScroll : function(e){
49179         this.syncScroll();
49180         var sb = this.scroller.dom;
49181         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49182         e.stopEvent();
49183     },
49184
49185     handleWheel : function(e){
49186         var d = e.getWheelDelta();
49187         this.scroller.dom.scrollTop -= d*22;
49188         // set this here to prevent jumpy scrolling on large tables
49189         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49190         e.stopEvent();
49191     },
49192
49193     renderRows : function(startRow, endRow){
49194         // pull in all the crap needed to render rows
49195         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49196         var colCount = cm.getColumnCount();
49197
49198         if(ds.getCount() < 1){
49199             return ["", ""];
49200         }
49201
49202         // build a map for all the columns
49203         var cs = [];
49204         for(var i = 0; i < colCount; i++){
49205             var name = cm.getDataIndex(i);
49206             cs[i] = {
49207                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49208                 renderer : cm.getRenderer(i),
49209                 id : cm.getColumnId(i),
49210                 locked : cm.isLocked(i)
49211             };
49212         }
49213
49214         startRow = startRow || 0;
49215         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49216
49217         // records to render
49218         var rs = ds.getRange(startRow, endRow);
49219
49220         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49221     },
49222
49223     // As much as I hate to duplicate code, this was branched because FireFox really hates
49224     // [].join("") on strings. The performance difference was substantial enough to
49225     // branch this function
49226     doRender : Roo.isGecko ?
49227             function(cs, rs, ds, startRow, colCount, stripe){
49228                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49229                 // buffers
49230                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49231                 
49232                 var hasListener = this.grid.hasListener('rowclass');
49233                 var rowcfg = {};
49234                 for(var j = 0, len = rs.length; j < len; j++){
49235                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49236                     for(var i = 0; i < colCount; i++){
49237                         c = cs[i];
49238                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49239                         p.id = c.id;
49240                         p.css = p.attr = "";
49241                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49242                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49243                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49244                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49245                         }
49246                         var markup = ct.apply(p);
49247                         if(!c.locked){
49248                             cb+= markup;
49249                         }else{
49250                             lcb+= markup;
49251                         }
49252                     }
49253                     var alt = [];
49254                     if(stripe && ((rowIndex+1) % 2 == 0)){
49255                         alt.push("x-grid-row-alt")
49256                     }
49257                     if(r.dirty){
49258                         alt.push(  " x-grid-dirty-row");
49259                     }
49260                     rp.cells = lcb;
49261                     if(this.getRowClass){
49262                         alt.push(this.getRowClass(r, rowIndex));
49263                     }
49264                     if (hasListener) {
49265                         rowcfg = {
49266                              
49267                             record: r,
49268                             rowIndex : rowIndex,
49269                             rowClass : ''
49270                         }
49271                         this.grid.fireEvent('rowclass', this, rowcfg);
49272                         alt.push(rowcfg.rowClass);
49273                     }
49274                     rp.alt = alt.join(" ");
49275                     lbuf+= rt.apply(rp);
49276                     rp.cells = cb;
49277                     buf+=  rt.apply(rp);
49278                 }
49279                 return [lbuf, buf];
49280             } :
49281             function(cs, rs, ds, startRow, colCount, stripe){
49282                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49283                 // buffers
49284                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49285                 var hasListener = this.grid.hasListener('rowclass');
49286                 var rowcfg = {};
49287                 for(var j = 0, len = rs.length; j < len; j++){
49288                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49289                     for(var i = 0; i < colCount; i++){
49290                         c = cs[i];
49291                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49292                         p.id = c.id;
49293                         p.css = p.attr = "";
49294                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49295                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49296                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49297                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49298                         }
49299                         var markup = ct.apply(p);
49300                         if(!c.locked){
49301                             cb[cb.length] = markup;
49302                         }else{
49303                             lcb[lcb.length] = markup;
49304                         }
49305                     }
49306                     var alt = [];
49307                     if(stripe && ((rowIndex+1) % 2 == 0)){
49308                         alt.push( "x-grid-row-alt");
49309                     }
49310                     if(r.dirty){
49311                         alt.push(" x-grid-dirty-row");
49312                     }
49313                     rp.cells = lcb;
49314                     if(this.getRowClass){
49315                         alt.push( this.getRowClass(r, rowIndex));
49316                     }
49317                     if (hasListener) {
49318                         rowcfg = {
49319                              
49320                             record: r,
49321                             rowIndex : rowIndex,
49322                             rowClass : ''
49323                         }
49324                         this.grid.fireEvent('rowclass', this, rowcfg);
49325                         alt.push(rowcfg.rowClass);
49326                     }
49327                     rp.alt = alt.join(" ");
49328                     rp.cells = lcb.join("");
49329                     lbuf[lbuf.length] = rt.apply(rp);
49330                     rp.cells = cb.join("");
49331                     buf[buf.length] =  rt.apply(rp);
49332                 }
49333                 return [lbuf.join(""), buf.join("")];
49334             },
49335
49336     renderBody : function(){
49337         var markup = this.renderRows();
49338         var bt = this.templates.body;
49339         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49340     },
49341
49342     /**
49343      * Refreshes the grid
49344      * @param {Boolean} headersToo
49345      */
49346     refresh : function(headersToo){
49347         this.fireEvent("beforerefresh", this);
49348         this.grid.stopEditing();
49349         var result = this.renderBody();
49350         this.lockedBody.update(result[0]);
49351         this.mainBody.update(result[1]);
49352         if(headersToo === true){
49353             this.updateHeaders();
49354             this.updateColumns();
49355             this.updateSplitters();
49356             this.updateHeaderSortState();
49357         }
49358         this.syncRowHeights();
49359         this.layout();
49360         this.fireEvent("refresh", this);
49361     },
49362
49363     handleColumnMove : function(cm, oldIndex, newIndex){
49364         this.indexMap = null;
49365         var s = this.getScrollState();
49366         this.refresh(true);
49367         this.restoreScroll(s);
49368         this.afterMove(newIndex);
49369     },
49370
49371     afterMove : function(colIndex){
49372         if(this.enableMoveAnim && Roo.enableFx){
49373             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49374         }
49375         // if multisort - fix sortOrder, and reload..
49376         if (this.grid.dataSource.multiSort) {
49377             // the we can call sort again..
49378             var dm = this.grid.dataSource;
49379             var cm = this.grid.colModel;
49380             var so = [];
49381             for(var i = 0; i < cm.config.length; i++ ) {
49382                 
49383                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49384                     continue; // dont' bother, it's not in sort list or being set.
49385                 }
49386                 
49387                 so.push(cm.config[i].dataIndex);
49388             };
49389             dm.sortOrder = so;
49390             dm.load(dm.lastOptions);
49391             
49392             
49393         }
49394         
49395     },
49396
49397     updateCell : function(dm, rowIndex, dataIndex){
49398         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49399         if(typeof colIndex == "undefined"){ // not present in grid
49400             return;
49401         }
49402         var cm = this.grid.colModel;
49403         var cell = this.getCell(rowIndex, colIndex);
49404         var cellText = this.getCellText(rowIndex, colIndex);
49405
49406         var p = {
49407             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49408             id : cm.getColumnId(colIndex),
49409             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49410         };
49411         var renderer = cm.getRenderer(colIndex);
49412         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49413         if(typeof val == "undefined" || val === "") val = "&#160;";
49414         cellText.innerHTML = val;
49415         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49416         this.syncRowHeights(rowIndex, rowIndex);
49417     },
49418
49419     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49420         var maxWidth = 0;
49421         if(this.grid.autoSizeHeaders){
49422             var h = this.getHeaderCellMeasure(colIndex);
49423             maxWidth = Math.max(maxWidth, h.scrollWidth);
49424         }
49425         var tb, index;
49426         if(this.cm.isLocked(colIndex)){
49427             tb = this.getLockedTable();
49428             index = colIndex;
49429         }else{
49430             tb = this.getBodyTable();
49431             index = colIndex - this.cm.getLockedCount();
49432         }
49433         if(tb && tb.rows){
49434             var rows = tb.rows;
49435             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49436             for(var i = 0; i < stopIndex; i++){
49437                 var cell = rows[i].childNodes[index].firstChild;
49438                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49439             }
49440         }
49441         return maxWidth + /*margin for error in IE*/ 5;
49442     },
49443     /**
49444      * Autofit a column to its content.
49445      * @param {Number} colIndex
49446      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49447      */
49448      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49449          if(this.cm.isHidden(colIndex)){
49450              return; // can't calc a hidden column
49451          }
49452         if(forceMinSize){
49453             var cid = this.cm.getColumnId(colIndex);
49454             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49455            if(this.grid.autoSizeHeaders){
49456                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49457            }
49458         }
49459         var newWidth = this.calcColumnWidth(colIndex);
49460         this.cm.setColumnWidth(colIndex,
49461             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49462         if(!suppressEvent){
49463             this.grid.fireEvent("columnresize", colIndex, newWidth);
49464         }
49465     },
49466
49467     /**
49468      * Autofits all columns to their content and then expands to fit any extra space in the grid
49469      */
49470      autoSizeColumns : function(){
49471         var cm = this.grid.colModel;
49472         var colCount = cm.getColumnCount();
49473         for(var i = 0; i < colCount; i++){
49474             this.autoSizeColumn(i, true, true);
49475         }
49476         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49477             this.fitColumns();
49478         }else{
49479             this.updateColumns();
49480             this.layout();
49481         }
49482     },
49483
49484     /**
49485      * Autofits all columns to the grid's width proportionate with their current size
49486      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49487      */
49488     fitColumns : function(reserveScrollSpace){
49489         var cm = this.grid.colModel;
49490         var colCount = cm.getColumnCount();
49491         var cols = [];
49492         var width = 0;
49493         var i, w;
49494         for (i = 0; i < colCount; i++){
49495             if(!cm.isHidden(i) && !cm.isFixed(i)){
49496                 w = cm.getColumnWidth(i);
49497                 cols.push(i);
49498                 cols.push(w);
49499                 width += w;
49500             }
49501         }
49502         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49503         if(reserveScrollSpace){
49504             avail -= 17;
49505         }
49506         var frac = (avail - cm.getTotalWidth())/width;
49507         while (cols.length){
49508             w = cols.pop();
49509             i = cols.pop();
49510             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49511         }
49512         this.updateColumns();
49513         this.layout();
49514     },
49515
49516     onRowSelect : function(rowIndex){
49517         var row = this.getRowComposite(rowIndex);
49518         row.addClass("x-grid-row-selected");
49519     },
49520
49521     onRowDeselect : function(rowIndex){
49522         var row = this.getRowComposite(rowIndex);
49523         row.removeClass("x-grid-row-selected");
49524     },
49525
49526     onCellSelect : function(row, col){
49527         var cell = this.getCell(row, col);
49528         if(cell){
49529             Roo.fly(cell).addClass("x-grid-cell-selected");
49530         }
49531     },
49532
49533     onCellDeselect : function(row, col){
49534         var cell = this.getCell(row, col);
49535         if(cell){
49536             Roo.fly(cell).removeClass("x-grid-cell-selected");
49537         }
49538     },
49539
49540     updateHeaderSortState : function(){
49541         
49542         // sort state can be single { field: xxx, direction : yyy}
49543         // or   { xxx=>ASC , yyy : DESC ..... }
49544         
49545         var mstate = {};
49546         if (!this.ds.multiSort) { 
49547             var state = this.ds.getSortState();
49548             if(!state){
49549                 return;
49550             }
49551             mstate[state.field] = state.direction;
49552             // FIXME... - this is not used here.. but might be elsewhere..
49553             this.sortState = state;
49554             
49555         } else {
49556             mstate = this.ds.sortToggle;
49557         }
49558         //remove existing sort classes..
49559         
49560         var sc = this.sortClasses;
49561         var hds = this.el.select(this.headerSelector).removeClass(sc);
49562         
49563         for(var f in mstate) {
49564         
49565             var sortColumn = this.cm.findColumnIndex(f);
49566             
49567             if(sortColumn != -1){
49568                 var sortDir = mstate[f];        
49569                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49570             }
49571         }
49572         
49573          
49574         
49575     },
49576
49577
49578     handleHeaderClick : function(g, index){
49579         if(this.headersDisabled){
49580             return;
49581         }
49582         var dm = g.dataSource, cm = g.colModel;
49583         if(!cm.isSortable(index)){
49584             return;
49585         }
49586         g.stopEditing();
49587         
49588         if (dm.multiSort) {
49589             // update the sortOrder
49590             var so = [];
49591             for(var i = 0; i < cm.config.length; i++ ) {
49592                 
49593                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49594                     continue; // dont' bother, it's not in sort list or being set.
49595                 }
49596                 
49597                 so.push(cm.config[i].dataIndex);
49598             };
49599             dm.sortOrder = so;
49600         }
49601         
49602         
49603         dm.sort(cm.getDataIndex(index));
49604     },
49605
49606
49607     destroy : function(){
49608         if(this.colMenu){
49609             this.colMenu.removeAll();
49610             Roo.menu.MenuMgr.unregister(this.colMenu);
49611             this.colMenu.getEl().remove();
49612             delete this.colMenu;
49613         }
49614         if(this.hmenu){
49615             this.hmenu.removeAll();
49616             Roo.menu.MenuMgr.unregister(this.hmenu);
49617             this.hmenu.getEl().remove();
49618             delete this.hmenu;
49619         }
49620         if(this.grid.enableColumnMove){
49621             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49622             if(dds){
49623                 for(var dd in dds){
49624                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49625                         var elid = dds[dd].dragElId;
49626                         dds[dd].unreg();
49627                         Roo.get(elid).remove();
49628                     } else if(dds[dd].config.isTarget){
49629                         dds[dd].proxyTop.remove();
49630                         dds[dd].proxyBottom.remove();
49631                         dds[dd].unreg();
49632                     }
49633                     if(Roo.dd.DDM.locationCache[dd]){
49634                         delete Roo.dd.DDM.locationCache[dd];
49635                     }
49636                 }
49637                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49638             }
49639         }
49640         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49641         this.bind(null, null);
49642         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49643     },
49644
49645     handleLockChange : function(){
49646         this.refresh(true);
49647     },
49648
49649     onDenyColumnLock : function(){
49650
49651     },
49652
49653     onDenyColumnHide : function(){
49654
49655     },
49656
49657     handleHdMenuClick : function(item){
49658         var index = this.hdCtxIndex;
49659         var cm = this.cm, ds = this.ds;
49660         switch(item.id){
49661             case "asc":
49662                 ds.sort(cm.getDataIndex(index), "ASC");
49663                 break;
49664             case "desc":
49665                 ds.sort(cm.getDataIndex(index), "DESC");
49666                 break;
49667             case "lock":
49668                 var lc = cm.getLockedCount();
49669                 if(cm.getColumnCount(true) <= lc+1){
49670                     this.onDenyColumnLock();
49671                     return;
49672                 }
49673                 if(lc != index){
49674                     cm.setLocked(index, true, true);
49675                     cm.moveColumn(index, lc);
49676                     this.grid.fireEvent("columnmove", index, lc);
49677                 }else{
49678                     cm.setLocked(index, true);
49679                 }
49680             break;
49681             case "unlock":
49682                 var lc = cm.getLockedCount();
49683                 if((lc-1) != index){
49684                     cm.setLocked(index, false, true);
49685                     cm.moveColumn(index, lc-1);
49686                     this.grid.fireEvent("columnmove", index, lc-1);
49687                 }else{
49688                     cm.setLocked(index, false);
49689                 }
49690             break;
49691             default:
49692                 index = cm.getIndexById(item.id.substr(4));
49693                 if(index != -1){
49694                     if(item.checked && cm.getColumnCount(true) <= 1){
49695                         this.onDenyColumnHide();
49696                         return false;
49697                     }
49698                     cm.setHidden(index, item.checked);
49699                 }
49700         }
49701         return true;
49702     },
49703
49704     beforeColMenuShow : function(){
49705         var cm = this.cm,  colCount = cm.getColumnCount();
49706         this.colMenu.removeAll();
49707         for(var i = 0; i < colCount; i++){
49708             this.colMenu.add(new Roo.menu.CheckItem({
49709                 id: "col-"+cm.getColumnId(i),
49710                 text: cm.getColumnHeader(i),
49711                 checked: !cm.isHidden(i),
49712                 hideOnClick:false
49713             }));
49714         }
49715     },
49716
49717     handleHdCtx : function(g, index, e){
49718         e.stopEvent();
49719         var hd = this.getHeaderCell(index);
49720         this.hdCtxIndex = index;
49721         var ms = this.hmenu.items, cm = this.cm;
49722         ms.get("asc").setDisabled(!cm.isSortable(index));
49723         ms.get("desc").setDisabled(!cm.isSortable(index));
49724         if(this.grid.enableColLock !== false){
49725             ms.get("lock").setDisabled(cm.isLocked(index));
49726             ms.get("unlock").setDisabled(!cm.isLocked(index));
49727         }
49728         this.hmenu.show(hd, "tl-bl");
49729     },
49730
49731     handleHdOver : function(e){
49732         var hd = this.findHeaderCell(e.getTarget());
49733         if(hd && !this.headersDisabled){
49734             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49735                this.fly(hd).addClass("x-grid-hd-over");
49736             }
49737         }
49738     },
49739
49740     handleHdOut : function(e){
49741         var hd = this.findHeaderCell(e.getTarget());
49742         if(hd){
49743             this.fly(hd).removeClass("x-grid-hd-over");
49744         }
49745     },
49746
49747     handleSplitDblClick : function(e, t){
49748         var i = this.getCellIndex(t);
49749         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49750             this.autoSizeColumn(i, true);
49751             this.layout();
49752         }
49753     },
49754
49755     render : function(){
49756
49757         var cm = this.cm;
49758         var colCount = cm.getColumnCount();
49759
49760         if(this.grid.monitorWindowResize === true){
49761             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49762         }
49763         var header = this.renderHeaders();
49764         var body = this.templates.body.apply({rows:""});
49765         var html = this.templates.master.apply({
49766             lockedBody: body,
49767             body: body,
49768             lockedHeader: header[0],
49769             header: header[1]
49770         });
49771
49772         //this.updateColumns();
49773
49774         this.grid.getGridEl().dom.innerHTML = html;
49775
49776         this.initElements();
49777         
49778         // a kludge to fix the random scolling effect in webkit
49779         this.el.on("scroll", function() {
49780             this.el.dom.scrollTop=0; // hopefully not recursive..
49781         },this);
49782
49783         this.scroller.on("scroll", this.handleScroll, this);
49784         this.lockedBody.on("mousewheel", this.handleWheel, this);
49785         this.mainBody.on("mousewheel", this.handleWheel, this);
49786
49787         this.mainHd.on("mouseover", this.handleHdOver, this);
49788         this.mainHd.on("mouseout", this.handleHdOut, this);
49789         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49790                 {delegate: "."+this.splitClass});
49791
49792         this.lockedHd.on("mouseover", this.handleHdOver, this);
49793         this.lockedHd.on("mouseout", this.handleHdOut, this);
49794         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49795                 {delegate: "."+this.splitClass});
49796
49797         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49798             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49799         }
49800
49801         this.updateSplitters();
49802
49803         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49804             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49805             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49806         }
49807
49808         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49809             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49810             this.hmenu.add(
49811                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49812                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49813             );
49814             if(this.grid.enableColLock !== false){
49815                 this.hmenu.add('-',
49816                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49817                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49818                 );
49819             }
49820             if(this.grid.enableColumnHide !== false){
49821
49822                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49823                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49824                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49825
49826                 this.hmenu.add('-',
49827                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49828                 );
49829             }
49830             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49831
49832             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49833         }
49834
49835         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49836             this.dd = new Roo.grid.GridDragZone(this.grid, {
49837                 ddGroup : this.grid.ddGroup || 'GridDD'
49838             });
49839         }
49840
49841         /*
49842         for(var i = 0; i < colCount; i++){
49843             if(cm.isHidden(i)){
49844                 this.hideColumn(i);
49845             }
49846             if(cm.config[i].align){
49847                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49848                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49849             }
49850         }*/
49851         
49852         this.updateHeaderSortState();
49853
49854         this.beforeInitialResize();
49855         this.layout(true);
49856
49857         // two part rendering gives faster view to the user
49858         this.renderPhase2.defer(1, this);
49859     },
49860
49861     renderPhase2 : function(){
49862         // render the rows now
49863         this.refresh();
49864         if(this.grid.autoSizeColumns){
49865             this.autoSizeColumns();
49866         }
49867     },
49868
49869     beforeInitialResize : function(){
49870
49871     },
49872
49873     onColumnSplitterMoved : function(i, w){
49874         this.userResized = true;
49875         var cm = this.grid.colModel;
49876         cm.setColumnWidth(i, w, true);
49877         var cid = cm.getColumnId(i);
49878         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49879         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49880         this.updateSplitters();
49881         this.layout();
49882         this.grid.fireEvent("columnresize", i, w);
49883     },
49884
49885     syncRowHeights : function(startIndex, endIndex){
49886         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49887             startIndex = startIndex || 0;
49888             var mrows = this.getBodyTable().rows;
49889             var lrows = this.getLockedTable().rows;
49890             var len = mrows.length-1;
49891             endIndex = Math.min(endIndex || len, len);
49892             for(var i = startIndex; i <= endIndex; i++){
49893                 var m = mrows[i], l = lrows[i];
49894                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49895                 m.style.height = l.style.height = h + "px";
49896             }
49897         }
49898     },
49899
49900     layout : function(initialRender, is2ndPass){
49901         var g = this.grid;
49902         var auto = g.autoHeight;
49903         var scrollOffset = 16;
49904         var c = g.getGridEl(), cm = this.cm,
49905                 expandCol = g.autoExpandColumn,
49906                 gv = this;
49907         //c.beginMeasure();
49908
49909         if(!c.dom.offsetWidth){ // display:none?
49910             if(initialRender){
49911                 this.lockedWrap.show();
49912                 this.mainWrap.show();
49913             }
49914             return;
49915         }
49916
49917         var hasLock = this.cm.isLocked(0);
49918
49919         var tbh = this.headerPanel.getHeight();
49920         var bbh = this.footerPanel.getHeight();
49921
49922         if(auto){
49923             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49924             var newHeight = ch + c.getBorderWidth("tb");
49925             if(g.maxHeight){
49926                 newHeight = Math.min(g.maxHeight, newHeight);
49927             }
49928             c.setHeight(newHeight);
49929         }
49930
49931         if(g.autoWidth){
49932             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49933         }
49934
49935         var s = this.scroller;
49936
49937         var csize = c.getSize(true);
49938
49939         this.el.setSize(csize.width, csize.height);
49940
49941         this.headerPanel.setWidth(csize.width);
49942         this.footerPanel.setWidth(csize.width);
49943
49944         var hdHeight = this.mainHd.getHeight();
49945         var vw = csize.width;
49946         var vh = csize.height - (tbh + bbh);
49947
49948         s.setSize(vw, vh);
49949
49950         var bt = this.getBodyTable();
49951         var ltWidth = hasLock ?
49952                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49953
49954         var scrollHeight = bt.offsetHeight;
49955         var scrollWidth = ltWidth + bt.offsetWidth;
49956         var vscroll = false, hscroll = false;
49957
49958         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49959
49960         var lw = this.lockedWrap, mw = this.mainWrap;
49961         var lb = this.lockedBody, mb = this.mainBody;
49962
49963         setTimeout(function(){
49964             var t = s.dom.offsetTop;
49965             var w = s.dom.clientWidth,
49966                 h = s.dom.clientHeight;
49967
49968             lw.setTop(t);
49969             lw.setSize(ltWidth, h);
49970
49971             mw.setLeftTop(ltWidth, t);
49972             mw.setSize(w-ltWidth, h);
49973
49974             lb.setHeight(h-hdHeight);
49975             mb.setHeight(h-hdHeight);
49976
49977             if(is2ndPass !== true && !gv.userResized && expandCol){
49978                 // high speed resize without full column calculation
49979                 
49980                 var ci = cm.getIndexById(expandCol);
49981                 if (ci < 0) {
49982                     ci = cm.findColumnIndex(expandCol);
49983                 }
49984                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49985                 var expandId = cm.getColumnId(ci);
49986                 var  tw = cm.getTotalWidth(false);
49987                 var currentWidth = cm.getColumnWidth(ci);
49988                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49989                 if(currentWidth != cw){
49990                     cm.setColumnWidth(ci, cw, true);
49991                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49992                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49993                     gv.updateSplitters();
49994                     gv.layout(false, true);
49995                 }
49996             }
49997
49998             if(initialRender){
49999                 lw.show();
50000                 mw.show();
50001             }
50002             //c.endMeasure();
50003         }, 10);
50004     },
50005
50006     onWindowResize : function(){
50007         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50008             return;
50009         }
50010         this.layout();
50011     },
50012
50013     appendFooter : function(parentEl){
50014         return null;
50015     },
50016
50017     sortAscText : "Sort Ascending",
50018     sortDescText : "Sort Descending",
50019     lockText : "Lock Column",
50020     unlockText : "Unlock Column",
50021     columnsText : "Columns"
50022 });
50023
50024
50025 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50026     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50027     this.proxy.el.addClass('x-grid3-col-dd');
50028 };
50029
50030 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50031     handleMouseDown : function(e){
50032
50033     },
50034
50035     callHandleMouseDown : function(e){
50036         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50037     }
50038 });
50039 /*
50040  * Based on:
50041  * Ext JS Library 1.1.1
50042  * Copyright(c) 2006-2007, Ext JS, LLC.
50043  *
50044  * Originally Released Under LGPL - original licence link has changed is not relivant.
50045  *
50046  * Fork - LGPL
50047  * <script type="text/javascript">
50048  */
50049  
50050 // private
50051 // This is a support class used internally by the Grid components
50052 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50053     this.grid = grid;
50054     this.view = grid.getView();
50055     this.proxy = this.view.resizeProxy;
50056     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50057         "gridSplitters" + this.grid.getGridEl().id, {
50058         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50059     });
50060     this.setHandleElId(Roo.id(hd));
50061     this.setOuterHandleElId(Roo.id(hd2));
50062     this.scroll = false;
50063 };
50064 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50065     fly: Roo.Element.fly,
50066
50067     b4StartDrag : function(x, y){
50068         this.view.headersDisabled = true;
50069         this.proxy.setHeight(this.view.mainWrap.getHeight());
50070         var w = this.cm.getColumnWidth(this.cellIndex);
50071         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50072         this.resetConstraints();
50073         this.setXConstraint(minw, 1000);
50074         this.setYConstraint(0, 0);
50075         this.minX = x - minw;
50076         this.maxX = x + 1000;
50077         this.startPos = x;
50078         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50079     },
50080
50081
50082     handleMouseDown : function(e){
50083         ev = Roo.EventObject.setEvent(e);
50084         var t = this.fly(ev.getTarget());
50085         if(t.hasClass("x-grid-split")){
50086             this.cellIndex = this.view.getCellIndex(t.dom);
50087             this.split = t.dom;
50088             this.cm = this.grid.colModel;
50089             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50090                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50091             }
50092         }
50093     },
50094
50095     endDrag : function(e){
50096         this.view.headersDisabled = false;
50097         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50098         var diff = endX - this.startPos;
50099         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50100     },
50101
50102     autoOffset : function(){
50103         this.setDelta(0,0);
50104     }
50105 });/*
50106  * Based on:
50107  * Ext JS Library 1.1.1
50108  * Copyright(c) 2006-2007, Ext JS, LLC.
50109  *
50110  * Originally Released Under LGPL - original licence link has changed is not relivant.
50111  *
50112  * Fork - LGPL
50113  * <script type="text/javascript">
50114  */
50115  
50116 // private
50117 // This is a support class used internally by the Grid components
50118 Roo.grid.GridDragZone = function(grid, config){
50119     this.view = grid.getView();
50120     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50121     if(this.view.lockedBody){
50122         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50123         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50124     }
50125     this.scroll = false;
50126     this.grid = grid;
50127     this.ddel = document.createElement('div');
50128     this.ddel.className = 'x-grid-dd-wrap';
50129 };
50130
50131 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50132     ddGroup : "GridDD",
50133
50134     getDragData : function(e){
50135         var t = Roo.lib.Event.getTarget(e);
50136         var rowIndex = this.view.findRowIndex(t);
50137         if(rowIndex !== false){
50138             var sm = this.grid.selModel;
50139             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50140               //  sm.mouseDown(e, t);
50141             //}
50142             if (e.hasModifier()){
50143                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50144             }
50145             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50146         }
50147         return false;
50148     },
50149
50150     onInitDrag : function(e){
50151         var data = this.dragData;
50152         this.ddel.innerHTML = this.grid.getDragDropText();
50153         this.proxy.update(this.ddel);
50154         // fire start drag?
50155     },
50156
50157     afterRepair : function(){
50158         this.dragging = false;
50159     },
50160
50161     getRepairXY : function(e, data){
50162         return false;
50163     },
50164
50165     onEndDrag : function(data, e){
50166         // fire end drag?
50167     },
50168
50169     onValidDrop : function(dd, e, id){
50170         // fire drag drop?
50171         this.hideProxy();
50172     },
50173
50174     beforeInvalidDrop : function(e, id){
50175
50176     }
50177 });/*
50178  * Based on:
50179  * Ext JS Library 1.1.1
50180  * Copyright(c) 2006-2007, Ext JS, LLC.
50181  *
50182  * Originally Released Under LGPL - original licence link has changed is not relivant.
50183  *
50184  * Fork - LGPL
50185  * <script type="text/javascript">
50186  */
50187  
50188
50189 /**
50190  * @class Roo.grid.ColumnModel
50191  * @extends Roo.util.Observable
50192  * This is the default implementation of a ColumnModel used by the Grid. It defines
50193  * the columns in the grid.
50194  * <br>Usage:<br>
50195  <pre><code>
50196  var colModel = new Roo.grid.ColumnModel([
50197         {header: "Ticker", width: 60, sortable: true, locked: true},
50198         {header: "Company Name", width: 150, sortable: true},
50199         {header: "Market Cap.", width: 100, sortable: true},
50200         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50201         {header: "Employees", width: 100, sortable: true, resizable: false}
50202  ]);
50203  </code></pre>
50204  * <p>
50205  
50206  * The config options listed for this class are options which may appear in each
50207  * individual column definition.
50208  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50209  * @constructor
50210  * @param {Object} config An Array of column config objects. See this class's
50211  * config objects for details.
50212 */
50213 Roo.grid.ColumnModel = function(config){
50214         /**
50215      * The config passed into the constructor
50216      */
50217     this.config = config;
50218     this.lookup = {};
50219
50220     // if no id, create one
50221     // if the column does not have a dataIndex mapping,
50222     // map it to the order it is in the config
50223     for(var i = 0, len = config.length; i < len; i++){
50224         var c = config[i];
50225         if(typeof c.dataIndex == "undefined"){
50226             c.dataIndex = i;
50227         }
50228         if(typeof c.renderer == "string"){
50229             c.renderer = Roo.util.Format[c.renderer];
50230         }
50231         if(typeof c.id == "undefined"){
50232             c.id = Roo.id();
50233         }
50234         if(c.editor && c.editor.xtype){
50235             c.editor  = Roo.factory(c.editor, Roo.grid);
50236         }
50237         if(c.editor && c.editor.isFormField){
50238             c.editor = new Roo.grid.GridEditor(c.editor);
50239         }
50240         this.lookup[c.id] = c;
50241     }
50242
50243     /**
50244      * The width of columns which have no width specified (defaults to 100)
50245      * @type Number
50246      */
50247     this.defaultWidth = 100;
50248
50249     /**
50250      * Default sortable of columns which have no sortable specified (defaults to false)
50251      * @type Boolean
50252      */
50253     this.defaultSortable = false;
50254
50255     this.addEvents({
50256         /**
50257              * @event widthchange
50258              * Fires when the width of a column changes.
50259              * @param {ColumnModel} this
50260              * @param {Number} columnIndex The column index
50261              * @param {Number} newWidth The new width
50262              */
50263             "widthchange": true,
50264         /**
50265              * @event headerchange
50266              * Fires when the text of a header changes.
50267              * @param {ColumnModel} this
50268              * @param {Number} columnIndex The column index
50269              * @param {Number} newText The new header text
50270              */
50271             "headerchange": true,
50272         /**
50273              * @event hiddenchange
50274              * Fires when a column is hidden or "unhidden".
50275              * @param {ColumnModel} this
50276              * @param {Number} columnIndex The column index
50277              * @param {Boolean} hidden true if hidden, false otherwise
50278              */
50279             "hiddenchange": true,
50280             /**
50281          * @event columnmoved
50282          * Fires when a column is moved.
50283          * @param {ColumnModel} this
50284          * @param {Number} oldIndex
50285          * @param {Number} newIndex
50286          */
50287         "columnmoved" : true,
50288         /**
50289          * @event columlockchange
50290          * Fires when a column's locked state is changed
50291          * @param {ColumnModel} this
50292          * @param {Number} colIndex
50293          * @param {Boolean} locked true if locked
50294          */
50295         "columnlockchange" : true
50296     });
50297     Roo.grid.ColumnModel.superclass.constructor.call(this);
50298 };
50299 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50300     /**
50301      * @cfg {String} header The header text to display in the Grid view.
50302      */
50303     /**
50304      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50305      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50306      * specified, the column's index is used as an index into the Record's data Array.
50307      */
50308     /**
50309      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50310      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50311      */
50312     /**
50313      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50314      * Defaults to the value of the {@link #defaultSortable} property.
50315      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50316      */
50317     /**
50318      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50319      */
50320     /**
50321      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50322      */
50323     /**
50324      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50325      */
50326     /**
50327      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50328      */
50329     /**
50330      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50331      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50332      * default renderer uses the raw data value.
50333      */
50334        /**
50335      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50336      */
50337     /**
50338      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50339      */
50340
50341     /**
50342      * Returns the id of the column at the specified index.
50343      * @param {Number} index The column index
50344      * @return {String} the id
50345      */
50346     getColumnId : function(index){
50347         return this.config[index].id;
50348     },
50349
50350     /**
50351      * Returns the column for a specified id.
50352      * @param {String} id The column id
50353      * @return {Object} the column
50354      */
50355     getColumnById : function(id){
50356         return this.lookup[id];
50357     },
50358
50359     
50360     /**
50361      * Returns the column for a specified dataIndex.
50362      * @param {String} dataIndex The column dataIndex
50363      * @return {Object|Boolean} the column or false if not found
50364      */
50365     getColumnByDataIndex: function(dataIndex){
50366         var index = this.findColumnIndex(dataIndex);
50367         return index > -1 ? this.config[index] : false;
50368     },
50369     
50370     /**
50371      * Returns the index for a specified column id.
50372      * @param {String} id The column id
50373      * @return {Number} the index, or -1 if not found
50374      */
50375     getIndexById : function(id){
50376         for(var i = 0, len = this.config.length; i < len; i++){
50377             if(this.config[i].id == id){
50378                 return i;
50379             }
50380         }
50381         return -1;
50382     },
50383     
50384     /**
50385      * Returns the index for a specified column dataIndex.
50386      * @param {String} dataIndex The column dataIndex
50387      * @return {Number} the index, or -1 if not found
50388      */
50389     
50390     findColumnIndex : function(dataIndex){
50391         for(var i = 0, len = this.config.length; i < len; i++){
50392             if(this.config[i].dataIndex == dataIndex){
50393                 return i;
50394             }
50395         }
50396         return -1;
50397     },
50398     
50399     
50400     moveColumn : function(oldIndex, newIndex){
50401         var c = this.config[oldIndex];
50402         this.config.splice(oldIndex, 1);
50403         this.config.splice(newIndex, 0, c);
50404         this.dataMap = null;
50405         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50406     },
50407
50408     isLocked : function(colIndex){
50409         return this.config[colIndex].locked === true;
50410     },
50411
50412     setLocked : function(colIndex, value, suppressEvent){
50413         if(this.isLocked(colIndex) == value){
50414             return;
50415         }
50416         this.config[colIndex].locked = value;
50417         if(!suppressEvent){
50418             this.fireEvent("columnlockchange", this, colIndex, value);
50419         }
50420     },
50421
50422     getTotalLockedWidth : function(){
50423         var totalWidth = 0;
50424         for(var i = 0; i < this.config.length; i++){
50425             if(this.isLocked(i) && !this.isHidden(i)){
50426                 this.totalWidth += this.getColumnWidth(i);
50427             }
50428         }
50429         return totalWidth;
50430     },
50431
50432     getLockedCount : function(){
50433         for(var i = 0, len = this.config.length; i < len; i++){
50434             if(!this.isLocked(i)){
50435                 return i;
50436             }
50437         }
50438     },
50439
50440     /**
50441      * Returns the number of columns.
50442      * @return {Number}
50443      */
50444     getColumnCount : function(visibleOnly){
50445         if(visibleOnly === true){
50446             var c = 0;
50447             for(var i = 0, len = this.config.length; i < len; i++){
50448                 if(!this.isHidden(i)){
50449                     c++;
50450                 }
50451             }
50452             return c;
50453         }
50454         return this.config.length;
50455     },
50456
50457     /**
50458      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50459      * @param {Function} fn
50460      * @param {Object} scope (optional)
50461      * @return {Array} result
50462      */
50463     getColumnsBy : function(fn, scope){
50464         var r = [];
50465         for(var i = 0, len = this.config.length; i < len; i++){
50466             var c = this.config[i];
50467             if(fn.call(scope||this, c, i) === true){
50468                 r[r.length] = c;
50469             }
50470         }
50471         return r;
50472     },
50473
50474     /**
50475      * Returns true if the specified column is sortable.
50476      * @param {Number} col The column index
50477      * @return {Boolean}
50478      */
50479     isSortable : function(col){
50480         if(typeof this.config[col].sortable == "undefined"){
50481             return this.defaultSortable;
50482         }
50483         return this.config[col].sortable;
50484     },
50485
50486     /**
50487      * Returns the rendering (formatting) function defined for the column.
50488      * @param {Number} col The column index.
50489      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50490      */
50491     getRenderer : function(col){
50492         if(!this.config[col].renderer){
50493             return Roo.grid.ColumnModel.defaultRenderer;
50494         }
50495         return this.config[col].renderer;
50496     },
50497
50498     /**
50499      * Sets the rendering (formatting) function for a column.
50500      * @param {Number} col The column index
50501      * @param {Function} fn The function to use to process the cell's raw data
50502      * to return HTML markup for the grid view. The render function is called with
50503      * the following parameters:<ul>
50504      * <li>Data value.</li>
50505      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50506      * <li>css A CSS style string to apply to the table cell.</li>
50507      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50508      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50509      * <li>Row index</li>
50510      * <li>Column index</li>
50511      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50512      */
50513     setRenderer : function(col, fn){
50514         this.config[col].renderer = fn;
50515     },
50516
50517     /**
50518      * Returns the width for the specified column.
50519      * @param {Number} col The column index
50520      * @return {Number}
50521      */
50522     getColumnWidth : function(col){
50523         return this.config[col].width * 1 || this.defaultWidth;
50524     },
50525
50526     /**
50527      * Sets the width for a column.
50528      * @param {Number} col The column index
50529      * @param {Number} width The new width
50530      */
50531     setColumnWidth : function(col, width, suppressEvent){
50532         this.config[col].width = width;
50533         this.totalWidth = null;
50534         if(!suppressEvent){
50535              this.fireEvent("widthchange", this, col, width);
50536         }
50537     },
50538
50539     /**
50540      * Returns the total width of all columns.
50541      * @param {Boolean} includeHidden True to include hidden column widths
50542      * @return {Number}
50543      */
50544     getTotalWidth : function(includeHidden){
50545         if(!this.totalWidth){
50546             this.totalWidth = 0;
50547             for(var i = 0, len = this.config.length; i < len; i++){
50548                 if(includeHidden || !this.isHidden(i)){
50549                     this.totalWidth += this.getColumnWidth(i);
50550                 }
50551             }
50552         }
50553         return this.totalWidth;
50554     },
50555
50556     /**
50557      * Returns the header for the specified column.
50558      * @param {Number} col The column index
50559      * @return {String}
50560      */
50561     getColumnHeader : function(col){
50562         return this.config[col].header;
50563     },
50564
50565     /**
50566      * Sets the header for a column.
50567      * @param {Number} col The column index
50568      * @param {String} header The new header
50569      */
50570     setColumnHeader : function(col, header){
50571         this.config[col].header = header;
50572         this.fireEvent("headerchange", this, col, header);
50573     },
50574
50575     /**
50576      * Returns the tooltip for the specified column.
50577      * @param {Number} col The column index
50578      * @return {String}
50579      */
50580     getColumnTooltip : function(col){
50581             return this.config[col].tooltip;
50582     },
50583     /**
50584      * Sets the tooltip for a column.
50585      * @param {Number} col The column index
50586      * @param {String} tooltip The new tooltip
50587      */
50588     setColumnTooltip : function(col, tooltip){
50589             this.config[col].tooltip = tooltip;
50590     },
50591
50592     /**
50593      * Returns the dataIndex for the specified column.
50594      * @param {Number} col The column index
50595      * @return {Number}
50596      */
50597     getDataIndex : function(col){
50598         return this.config[col].dataIndex;
50599     },
50600
50601     /**
50602      * Sets the dataIndex for a column.
50603      * @param {Number} col The column index
50604      * @param {Number} dataIndex The new dataIndex
50605      */
50606     setDataIndex : function(col, dataIndex){
50607         this.config[col].dataIndex = dataIndex;
50608     },
50609
50610     
50611     
50612     /**
50613      * Returns true if the cell is editable.
50614      * @param {Number} colIndex The column index
50615      * @param {Number} rowIndex The row index
50616      * @return {Boolean}
50617      */
50618     isCellEditable : function(colIndex, rowIndex){
50619         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50620     },
50621
50622     /**
50623      * Returns the editor defined for the cell/column.
50624      * return false or null to disable editing.
50625      * @param {Number} colIndex The column index
50626      * @param {Number} rowIndex The row index
50627      * @return {Object}
50628      */
50629     getCellEditor : function(colIndex, rowIndex){
50630         return this.config[colIndex].editor;
50631     },
50632
50633     /**
50634      * Sets if a column is editable.
50635      * @param {Number} col The column index
50636      * @param {Boolean} editable True if the column is editable
50637      */
50638     setEditable : function(col, editable){
50639         this.config[col].editable = editable;
50640     },
50641
50642
50643     /**
50644      * Returns true if the column is hidden.
50645      * @param {Number} colIndex The column index
50646      * @return {Boolean}
50647      */
50648     isHidden : function(colIndex){
50649         return this.config[colIndex].hidden;
50650     },
50651
50652
50653     /**
50654      * Returns true if the column width cannot be changed
50655      */
50656     isFixed : function(colIndex){
50657         return this.config[colIndex].fixed;
50658     },
50659
50660     /**
50661      * Returns true if the column can be resized
50662      * @return {Boolean}
50663      */
50664     isResizable : function(colIndex){
50665         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50666     },
50667     /**
50668      * Sets if a column is hidden.
50669      * @param {Number} colIndex The column index
50670      * @param {Boolean} hidden True if the column is hidden
50671      */
50672     setHidden : function(colIndex, hidden){
50673         this.config[colIndex].hidden = hidden;
50674         this.totalWidth = null;
50675         this.fireEvent("hiddenchange", this, colIndex, hidden);
50676     },
50677
50678     /**
50679      * Sets the editor for a column.
50680      * @param {Number} col The column index
50681      * @param {Object} editor The editor object
50682      */
50683     setEditor : function(col, editor){
50684         this.config[col].editor = editor;
50685     }
50686 });
50687
50688 Roo.grid.ColumnModel.defaultRenderer = function(value){
50689         if(typeof value == "string" && value.length < 1){
50690             return "&#160;";
50691         }
50692         return value;
50693 };
50694
50695 // Alias for backwards compatibility
50696 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50697 /*
50698  * Based on:
50699  * Ext JS Library 1.1.1
50700  * Copyright(c) 2006-2007, Ext JS, LLC.
50701  *
50702  * Originally Released Under LGPL - original licence link has changed is not relivant.
50703  *
50704  * Fork - LGPL
50705  * <script type="text/javascript">
50706  */
50707
50708 /**
50709  * @class Roo.grid.AbstractSelectionModel
50710  * @extends Roo.util.Observable
50711  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50712  * implemented by descendant classes.  This class should not be directly instantiated.
50713  * @constructor
50714  */
50715 Roo.grid.AbstractSelectionModel = function(){
50716     this.locked = false;
50717     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50718 };
50719
50720 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50721     /** @ignore Called by the grid automatically. Do not call directly. */
50722     init : function(grid){
50723         this.grid = grid;
50724         this.initEvents();
50725     },
50726
50727     /**
50728      * Locks the selections.
50729      */
50730     lock : function(){
50731         this.locked = true;
50732     },
50733
50734     /**
50735      * Unlocks the selections.
50736      */
50737     unlock : function(){
50738         this.locked = false;
50739     },
50740
50741     /**
50742      * Returns true if the selections are locked.
50743      * @return {Boolean}
50744      */
50745     isLocked : function(){
50746         return this.locked;
50747     }
50748 });/*
50749  * Based on:
50750  * Ext JS Library 1.1.1
50751  * Copyright(c) 2006-2007, Ext JS, LLC.
50752  *
50753  * Originally Released Under LGPL - original licence link has changed is not relivant.
50754  *
50755  * Fork - LGPL
50756  * <script type="text/javascript">
50757  */
50758 /**
50759  * @extends Roo.grid.AbstractSelectionModel
50760  * @class Roo.grid.RowSelectionModel
50761  * The default SelectionModel used by {@link Roo.grid.Grid}.
50762  * It supports multiple selections and keyboard selection/navigation. 
50763  * @constructor
50764  * @param {Object} config
50765  */
50766 Roo.grid.RowSelectionModel = function(config){
50767     Roo.apply(this, config);
50768     this.selections = new Roo.util.MixedCollection(false, function(o){
50769         return o.id;
50770     });
50771
50772     this.last = false;
50773     this.lastActive = false;
50774
50775     this.addEvents({
50776         /**
50777              * @event selectionchange
50778              * Fires when the selection changes
50779              * @param {SelectionModel} this
50780              */
50781             "selectionchange" : true,
50782         /**
50783              * @event afterselectionchange
50784              * Fires after the selection changes (eg. by key press or clicking)
50785              * @param {SelectionModel} this
50786              */
50787             "afterselectionchange" : true,
50788         /**
50789              * @event beforerowselect
50790              * Fires when a row is selected being selected, return false to cancel.
50791              * @param {SelectionModel} this
50792              * @param {Number} rowIndex The selected index
50793              * @param {Boolean} keepExisting False if other selections will be cleared
50794              */
50795             "beforerowselect" : true,
50796         /**
50797              * @event rowselect
50798              * Fires when a row is selected.
50799              * @param {SelectionModel} this
50800              * @param {Number} rowIndex The selected index
50801              * @param {Roo.data.Record} r The record
50802              */
50803             "rowselect" : true,
50804         /**
50805              * @event rowdeselect
50806              * Fires when a row is deselected.
50807              * @param {SelectionModel} this
50808              * @param {Number} rowIndex The selected index
50809              */
50810         "rowdeselect" : true
50811     });
50812     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50813     this.locked = false;
50814 };
50815
50816 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50817     /**
50818      * @cfg {Boolean} singleSelect
50819      * True to allow selection of only one row at a time (defaults to false)
50820      */
50821     singleSelect : false,
50822
50823     // private
50824     initEvents : function(){
50825
50826         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50827             this.grid.on("mousedown", this.handleMouseDown, this);
50828         }else{ // allow click to work like normal
50829             this.grid.on("rowclick", this.handleDragableRowClick, this);
50830         }
50831
50832         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50833             "up" : function(e){
50834                 if(!e.shiftKey){
50835                     this.selectPrevious(e.shiftKey);
50836                 }else if(this.last !== false && this.lastActive !== false){
50837                     var last = this.last;
50838                     this.selectRange(this.last,  this.lastActive-1);
50839                     this.grid.getView().focusRow(this.lastActive);
50840                     if(last !== false){
50841                         this.last = last;
50842                     }
50843                 }else{
50844                     this.selectFirstRow();
50845                 }
50846                 this.fireEvent("afterselectionchange", this);
50847             },
50848             "down" : function(e){
50849                 if(!e.shiftKey){
50850                     this.selectNext(e.shiftKey);
50851                 }else if(this.last !== false && this.lastActive !== false){
50852                     var last = this.last;
50853                     this.selectRange(this.last,  this.lastActive+1);
50854                     this.grid.getView().focusRow(this.lastActive);
50855                     if(last !== false){
50856                         this.last = last;
50857                     }
50858                 }else{
50859                     this.selectFirstRow();
50860                 }
50861                 this.fireEvent("afterselectionchange", this);
50862             },
50863             scope: this
50864         });
50865
50866         var view = this.grid.view;
50867         view.on("refresh", this.onRefresh, this);
50868         view.on("rowupdated", this.onRowUpdated, this);
50869         view.on("rowremoved", this.onRemove, this);
50870     },
50871
50872     // private
50873     onRefresh : function(){
50874         var ds = this.grid.dataSource, i, v = this.grid.view;
50875         var s = this.selections;
50876         s.each(function(r){
50877             if((i = ds.indexOfId(r.id)) != -1){
50878                 v.onRowSelect(i);
50879             }else{
50880                 s.remove(r);
50881             }
50882         });
50883     },
50884
50885     // private
50886     onRemove : function(v, index, r){
50887         this.selections.remove(r);
50888     },
50889
50890     // private
50891     onRowUpdated : function(v, index, r){
50892         if(this.isSelected(r)){
50893             v.onRowSelect(index);
50894         }
50895     },
50896
50897     /**
50898      * Select records.
50899      * @param {Array} records The records to select
50900      * @param {Boolean} keepExisting (optional) True to keep existing selections
50901      */
50902     selectRecords : function(records, keepExisting){
50903         if(!keepExisting){
50904             this.clearSelections();
50905         }
50906         var ds = this.grid.dataSource;
50907         for(var i = 0, len = records.length; i < len; i++){
50908             this.selectRow(ds.indexOf(records[i]), true);
50909         }
50910     },
50911
50912     /**
50913      * Gets the number of selected rows.
50914      * @return {Number}
50915      */
50916     getCount : function(){
50917         return this.selections.length;
50918     },
50919
50920     /**
50921      * Selects the first row in the grid.
50922      */
50923     selectFirstRow : function(){
50924         this.selectRow(0);
50925     },
50926
50927     /**
50928      * Select the last row.
50929      * @param {Boolean} keepExisting (optional) True to keep existing selections
50930      */
50931     selectLastRow : function(keepExisting){
50932         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50933     },
50934
50935     /**
50936      * Selects the row immediately following the last selected row.
50937      * @param {Boolean} keepExisting (optional) True to keep existing selections
50938      */
50939     selectNext : function(keepExisting){
50940         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50941             this.selectRow(this.last+1, keepExisting);
50942             this.grid.getView().focusRow(this.last);
50943         }
50944     },
50945
50946     /**
50947      * Selects the row that precedes the last selected row.
50948      * @param {Boolean} keepExisting (optional) True to keep existing selections
50949      */
50950     selectPrevious : function(keepExisting){
50951         if(this.last){
50952             this.selectRow(this.last-1, keepExisting);
50953             this.grid.getView().focusRow(this.last);
50954         }
50955     },
50956
50957     /**
50958      * Returns the selected records
50959      * @return {Array} Array of selected records
50960      */
50961     getSelections : function(){
50962         return [].concat(this.selections.items);
50963     },
50964
50965     /**
50966      * Returns the first selected record.
50967      * @return {Record}
50968      */
50969     getSelected : function(){
50970         return this.selections.itemAt(0);
50971     },
50972
50973
50974     /**
50975      * Clears all selections.
50976      */
50977     clearSelections : function(fast){
50978         if(this.locked) return;
50979         if(fast !== true){
50980             var ds = this.grid.dataSource;
50981             var s = this.selections;
50982             s.each(function(r){
50983                 this.deselectRow(ds.indexOfId(r.id));
50984             }, this);
50985             s.clear();
50986         }else{
50987             this.selections.clear();
50988         }
50989         this.last = false;
50990     },
50991
50992
50993     /**
50994      * Selects all rows.
50995      */
50996     selectAll : function(){
50997         if(this.locked) return;
50998         this.selections.clear();
50999         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51000             this.selectRow(i, true);
51001         }
51002     },
51003
51004     /**
51005      * Returns True if there is a selection.
51006      * @return {Boolean}
51007      */
51008     hasSelection : function(){
51009         return this.selections.length > 0;
51010     },
51011
51012     /**
51013      * Returns True if the specified row is selected.
51014      * @param {Number/Record} record The record or index of the record to check
51015      * @return {Boolean}
51016      */
51017     isSelected : function(index){
51018         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51019         return (r && this.selections.key(r.id) ? true : false);
51020     },
51021
51022     /**
51023      * Returns True if the specified record id is selected.
51024      * @param {String} id The id of record to check
51025      * @return {Boolean}
51026      */
51027     isIdSelected : function(id){
51028         return (this.selections.key(id) ? true : false);
51029     },
51030
51031     // private
51032     handleMouseDown : function(e, t){
51033         var view = this.grid.getView(), rowIndex;
51034         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51035             return;
51036         };
51037         if(e.shiftKey && this.last !== false){
51038             var last = this.last;
51039             this.selectRange(last, rowIndex, e.ctrlKey);
51040             this.last = last; // reset the last
51041             view.focusRow(rowIndex);
51042         }else{
51043             var isSelected = this.isSelected(rowIndex);
51044             if(e.button !== 0 && isSelected){
51045                 view.focusRow(rowIndex);
51046             }else if(e.ctrlKey && isSelected){
51047                 this.deselectRow(rowIndex);
51048             }else if(!isSelected){
51049                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51050                 view.focusRow(rowIndex);
51051             }
51052         }
51053         this.fireEvent("afterselectionchange", this);
51054     },
51055     // private
51056     handleDragableRowClick :  function(grid, rowIndex, e) 
51057     {
51058         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51059             this.selectRow(rowIndex, false);
51060             grid.view.focusRow(rowIndex);
51061              this.fireEvent("afterselectionchange", this);
51062         }
51063     },
51064     
51065     /**
51066      * Selects multiple rows.
51067      * @param {Array} rows Array of the indexes of the row to select
51068      * @param {Boolean} keepExisting (optional) True to keep existing selections
51069      */
51070     selectRows : function(rows, keepExisting){
51071         if(!keepExisting){
51072             this.clearSelections();
51073         }
51074         for(var i = 0, len = rows.length; i < len; i++){
51075             this.selectRow(rows[i], true);
51076         }
51077     },
51078
51079     /**
51080      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51081      * @param {Number} startRow The index of the first row in the range
51082      * @param {Number} endRow The index of the last row in the range
51083      * @param {Boolean} keepExisting (optional) True to retain existing selections
51084      */
51085     selectRange : function(startRow, endRow, keepExisting){
51086         if(this.locked) return;
51087         if(!keepExisting){
51088             this.clearSelections();
51089         }
51090         if(startRow <= endRow){
51091             for(var i = startRow; i <= endRow; i++){
51092                 this.selectRow(i, true);
51093             }
51094         }else{
51095             for(var i = startRow; i >= endRow; i--){
51096                 this.selectRow(i, true);
51097             }
51098         }
51099     },
51100
51101     /**
51102      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51103      * @param {Number} startRow The index of the first row in the range
51104      * @param {Number} endRow The index of the last row in the range
51105      */
51106     deselectRange : function(startRow, endRow, preventViewNotify){
51107         if(this.locked) return;
51108         for(var i = startRow; i <= endRow; i++){
51109             this.deselectRow(i, preventViewNotify);
51110         }
51111     },
51112
51113     /**
51114      * Selects a row.
51115      * @param {Number} row The index of the row to select
51116      * @param {Boolean} keepExisting (optional) True to keep existing selections
51117      */
51118     selectRow : function(index, keepExisting, preventViewNotify){
51119         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51120         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51121             if(!keepExisting || this.singleSelect){
51122                 this.clearSelections();
51123             }
51124             var r = this.grid.dataSource.getAt(index);
51125             this.selections.add(r);
51126             this.last = this.lastActive = index;
51127             if(!preventViewNotify){
51128                 this.grid.getView().onRowSelect(index);
51129             }
51130             this.fireEvent("rowselect", this, index, r);
51131             this.fireEvent("selectionchange", this);
51132         }
51133     },
51134
51135     /**
51136      * Deselects a row.
51137      * @param {Number} row The index of the row to deselect
51138      */
51139     deselectRow : function(index, preventViewNotify){
51140         if(this.locked) return;
51141         if(this.last == index){
51142             this.last = false;
51143         }
51144         if(this.lastActive == index){
51145             this.lastActive = false;
51146         }
51147         var r = this.grid.dataSource.getAt(index);
51148         this.selections.remove(r);
51149         if(!preventViewNotify){
51150             this.grid.getView().onRowDeselect(index);
51151         }
51152         this.fireEvent("rowdeselect", this, index);
51153         this.fireEvent("selectionchange", this);
51154     },
51155
51156     // private
51157     restoreLast : function(){
51158         if(this._last){
51159             this.last = this._last;
51160         }
51161     },
51162
51163     // private
51164     acceptsNav : function(row, col, cm){
51165         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51166     },
51167
51168     // private
51169     onEditorKey : function(field, e){
51170         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51171         if(k == e.TAB){
51172             e.stopEvent();
51173             ed.completeEdit();
51174             if(e.shiftKey){
51175                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51176             }else{
51177                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51178             }
51179         }else if(k == e.ENTER && !e.ctrlKey){
51180             e.stopEvent();
51181             ed.completeEdit();
51182             if(e.shiftKey){
51183                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51184             }else{
51185                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51186             }
51187         }else if(k == e.ESC){
51188             ed.cancelEdit();
51189         }
51190         if(newCell){
51191             g.startEditing(newCell[0], newCell[1]);
51192         }
51193     }
51194 });/*
51195  * Based on:
51196  * Ext JS Library 1.1.1
51197  * Copyright(c) 2006-2007, Ext JS, LLC.
51198  *
51199  * Originally Released Under LGPL - original licence link has changed is not relivant.
51200  *
51201  * Fork - LGPL
51202  * <script type="text/javascript">
51203  */
51204 /**
51205  * @class Roo.grid.CellSelectionModel
51206  * @extends Roo.grid.AbstractSelectionModel
51207  * This class provides the basic implementation for cell selection in a grid.
51208  * @constructor
51209  * @param {Object} config The object containing the configuration of this model.
51210  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51211  */
51212 Roo.grid.CellSelectionModel = function(config){
51213     Roo.apply(this, config);
51214
51215     this.selection = null;
51216
51217     this.addEvents({
51218         /**
51219              * @event beforerowselect
51220              * Fires before a cell is selected.
51221              * @param {SelectionModel} this
51222              * @param {Number} rowIndex The selected row index
51223              * @param {Number} colIndex The selected cell index
51224              */
51225             "beforecellselect" : true,
51226         /**
51227              * @event cellselect
51228              * Fires when a cell is selected.
51229              * @param {SelectionModel} this
51230              * @param {Number} rowIndex The selected row index
51231              * @param {Number} colIndex The selected cell index
51232              */
51233             "cellselect" : true,
51234         /**
51235              * @event selectionchange
51236              * Fires when the active selection changes.
51237              * @param {SelectionModel} this
51238              * @param {Object} selection null for no selection or an object (o) with two properties
51239                 <ul>
51240                 <li>o.record: the record object for the row the selection is in</li>
51241                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51242                 </ul>
51243              */
51244             "selectionchange" : true,
51245         /**
51246              * @event tabend
51247              * Fires when the tab (or enter) was pressed on the last editable cell
51248              * You can use this to trigger add new row.
51249              * @param {SelectionModel} this
51250              */
51251             "tabend" : true
51252     });
51253     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51254 };
51255
51256 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51257     
51258     enter_is_tab: false,
51259
51260     /** @ignore */
51261     initEvents : function(){
51262         this.grid.on("mousedown", this.handleMouseDown, this);
51263         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51264         var view = this.grid.view;
51265         view.on("refresh", this.onViewChange, this);
51266         view.on("rowupdated", this.onRowUpdated, this);
51267         view.on("beforerowremoved", this.clearSelections, this);
51268         view.on("beforerowsinserted", this.clearSelections, this);
51269         if(this.grid.isEditor){
51270             this.grid.on("beforeedit", this.beforeEdit,  this);
51271         }
51272     },
51273
51274         //private
51275     beforeEdit : function(e){
51276         this.select(e.row, e.column, false, true, e.record);
51277     },
51278
51279         //private
51280     onRowUpdated : function(v, index, r){
51281         if(this.selection && this.selection.record == r){
51282             v.onCellSelect(index, this.selection.cell[1]);
51283         }
51284     },
51285
51286         //private
51287     onViewChange : function(){
51288         this.clearSelections(true);
51289     },
51290
51291         /**
51292          * Returns the currently selected cell,.
51293          * @return {Array} The selected cell (row, column) or null if none selected.
51294          */
51295     getSelectedCell : function(){
51296         return this.selection ? this.selection.cell : null;
51297     },
51298
51299     /**
51300      * Clears all selections.
51301      * @param {Boolean} true to prevent the gridview from being notified about the change.
51302      */
51303     clearSelections : function(preventNotify){
51304         var s = this.selection;
51305         if(s){
51306             if(preventNotify !== true){
51307                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51308             }
51309             this.selection = null;
51310             this.fireEvent("selectionchange", this, null);
51311         }
51312     },
51313
51314     /**
51315      * Returns true if there is a selection.
51316      * @return {Boolean}
51317      */
51318     hasSelection : function(){
51319         return this.selection ? true : false;
51320     },
51321
51322     /** @ignore */
51323     handleMouseDown : function(e, t){
51324         var v = this.grid.getView();
51325         if(this.isLocked()){
51326             return;
51327         };
51328         var row = v.findRowIndex(t);
51329         var cell = v.findCellIndex(t);
51330         if(row !== false && cell !== false){
51331             this.select(row, cell);
51332         }
51333     },
51334
51335     /**
51336      * Selects a cell.
51337      * @param {Number} rowIndex
51338      * @param {Number} collIndex
51339      */
51340     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51341         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51342             this.clearSelections();
51343             r = r || this.grid.dataSource.getAt(rowIndex);
51344             this.selection = {
51345                 record : r,
51346                 cell : [rowIndex, colIndex]
51347             };
51348             if(!preventViewNotify){
51349                 var v = this.grid.getView();
51350                 v.onCellSelect(rowIndex, colIndex);
51351                 if(preventFocus !== true){
51352                     v.focusCell(rowIndex, colIndex);
51353                 }
51354             }
51355             this.fireEvent("cellselect", this, rowIndex, colIndex);
51356             this.fireEvent("selectionchange", this, this.selection);
51357         }
51358     },
51359
51360         //private
51361     isSelectable : function(rowIndex, colIndex, cm){
51362         return !cm.isHidden(colIndex);
51363     },
51364
51365     /** @ignore */
51366     handleKeyDown : function(e){
51367         //Roo.log('Cell Sel Model handleKeyDown');
51368         if(!e.isNavKeyPress()){
51369             return;
51370         }
51371         var g = this.grid, s = this.selection;
51372         if(!s){
51373             e.stopEvent();
51374             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51375             if(cell){
51376                 this.select(cell[0], cell[1]);
51377             }
51378             return;
51379         }
51380         var sm = this;
51381         var walk = function(row, col, step){
51382             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51383         };
51384         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51385         var newCell;
51386
51387       
51388
51389         switch(k){
51390             case e.TAB:
51391                 // handled by onEditorKey
51392                 if (g.isEditor && g.editing) {
51393                     return;
51394                 }
51395                 if(e.shiftKey) {
51396                     newCell = walk(r, c-1, -1);
51397                 } else {
51398                     newCell = walk(r, c+1, 1);
51399                 }
51400                 break;
51401             
51402             case e.DOWN:
51403                newCell = walk(r+1, c, 1);
51404                 break;
51405             
51406             case e.UP:
51407                 newCell = walk(r-1, c, -1);
51408                 break;
51409             
51410             case e.RIGHT:
51411                 newCell = walk(r, c+1, 1);
51412                 break;
51413             
51414             case e.LEFT:
51415                 newCell = walk(r, c-1, -1);
51416                 break;
51417             
51418             case e.ENTER:
51419                 
51420                 if(g.isEditor && !g.editing){
51421                    g.startEditing(r, c);
51422                    e.stopEvent();
51423                    return;
51424                 }
51425                 
51426                 
51427              break;
51428         };
51429         if(newCell){
51430             this.select(newCell[0], newCell[1]);
51431             e.stopEvent();
51432             
51433         }
51434     },
51435
51436     acceptsNav : function(row, col, cm){
51437         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51438     },
51439     /**
51440      * Selects a cell.
51441      * @param {Number} field (not used) - as it's normally used as a listener
51442      * @param {Number} e - event - fake it by using
51443      *
51444      * var e = Roo.EventObjectImpl.prototype;
51445      * e.keyCode = e.TAB
51446      *
51447      * 
51448      */
51449     onEditorKey : function(field, e){
51450         
51451         var k = e.getKey(),
51452             newCell,
51453             g = this.grid,
51454             ed = g.activeEditor,
51455             forward = false;
51456         ///Roo.log('onEditorKey' + k);
51457         
51458         
51459         if (this.enter_is_tab && k == e.ENTER) {
51460             k = e.TAB;
51461         }
51462         
51463         if(k == e.TAB){
51464             if(e.shiftKey){
51465                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51466             }else{
51467                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51468                 forward = true;
51469             }
51470             
51471             e.stopEvent();
51472             
51473         }else if(k == e.ENTER &&  !e.ctrlKey){
51474             ed.completeEdit();
51475             e.stopEvent();
51476             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51477         }else if(k == e.ESC){
51478             ed.cancelEdit();
51479         }
51480         
51481         
51482         if(newCell){
51483             //Roo.log('next cell after edit');
51484             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51485         } else if (forward) {
51486             // tabbed past last
51487             this.fireEvent.defer(100, this, ['tabend',this]);
51488         }
51489     }
51490 });/*
51491  * Based on:
51492  * Ext JS Library 1.1.1
51493  * Copyright(c) 2006-2007, Ext JS, LLC.
51494  *
51495  * Originally Released Under LGPL - original licence link has changed is not relivant.
51496  *
51497  * Fork - LGPL
51498  * <script type="text/javascript">
51499  */
51500  
51501 /**
51502  * @class Roo.grid.EditorGrid
51503  * @extends Roo.grid.Grid
51504  * Class for creating and editable grid.
51505  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51506  * The container MUST have some type of size defined for the grid to fill. The container will be 
51507  * automatically set to position relative if it isn't already.
51508  * @param {Object} dataSource The data model to bind to
51509  * @param {Object} colModel The column model with info about this grid's columns
51510  */
51511 Roo.grid.EditorGrid = function(container, config){
51512     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51513     this.getGridEl().addClass("xedit-grid");
51514
51515     if(!this.selModel){
51516         this.selModel = new Roo.grid.CellSelectionModel();
51517     }
51518
51519     this.activeEditor = null;
51520
51521         this.addEvents({
51522             /**
51523              * @event beforeedit
51524              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51525              * <ul style="padding:5px;padding-left:16px;">
51526              * <li>grid - This grid</li>
51527              * <li>record - The record being edited</li>
51528              * <li>field - The field name being edited</li>
51529              * <li>value - The value for the field being edited.</li>
51530              * <li>row - The grid row index</li>
51531              * <li>column - The grid column index</li>
51532              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51533              * </ul>
51534              * @param {Object} e An edit event (see above for description)
51535              */
51536             "beforeedit" : true,
51537             /**
51538              * @event afteredit
51539              * Fires after a cell is edited. <br />
51540              * <ul style="padding:5px;padding-left:16px;">
51541              * <li>grid - This grid</li>
51542              * <li>record - The record being edited</li>
51543              * <li>field - The field name being edited</li>
51544              * <li>value - The value being set</li>
51545              * <li>originalValue - The original value for the field, before the edit.</li>
51546              * <li>row - The grid row index</li>
51547              * <li>column - The grid column index</li>
51548              * </ul>
51549              * @param {Object} e An edit event (see above for description)
51550              */
51551             "afteredit" : true,
51552             /**
51553              * @event validateedit
51554              * Fires after a cell is edited, but before the value is set in the record. 
51555          * You can use this to modify the value being set in the field, Return false
51556              * to cancel the change. The edit event object has the following properties <br />
51557              * <ul style="padding:5px;padding-left:16px;">
51558          * <li>editor - This editor</li>
51559              * <li>grid - This grid</li>
51560              * <li>record - The record being edited</li>
51561              * <li>field - The field name being edited</li>
51562              * <li>value - The value being set</li>
51563              * <li>originalValue - The original value for the field, before the edit.</li>
51564              * <li>row - The grid row index</li>
51565              * <li>column - The grid column index</li>
51566              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51567              * </ul>
51568              * @param {Object} e An edit event (see above for description)
51569              */
51570             "validateedit" : true
51571         });
51572     this.on("bodyscroll", this.stopEditing,  this);
51573     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51574 };
51575
51576 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51577     /**
51578      * @cfg {Number} clicksToEdit
51579      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51580      */
51581     clicksToEdit: 2,
51582
51583     // private
51584     isEditor : true,
51585     // private
51586     trackMouseOver: false, // causes very odd FF errors
51587
51588     onCellDblClick : function(g, row, col){
51589         this.startEditing(row, col);
51590     },
51591
51592     onEditComplete : function(ed, value, startValue){
51593         this.editing = false;
51594         this.activeEditor = null;
51595         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51596         var r = ed.record;
51597         var field = this.colModel.getDataIndex(ed.col);
51598         var e = {
51599             grid: this,
51600             record: r,
51601             field: field,
51602             originalValue: startValue,
51603             value: value,
51604             row: ed.row,
51605             column: ed.col,
51606             cancel:false,
51607             editor: ed
51608         };
51609         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51610         cell.show();
51611           
51612         if(String(value) !== String(startValue)){
51613             
51614             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51615                 r.set(field, e.value);
51616                 // if we are dealing with a combo box..
51617                 // then we also set the 'name' colum to be the displayField
51618                 if (ed.field.displayField && ed.field.name) {
51619                     r.set(ed.field.name, ed.field.el.dom.value);
51620                 }
51621                 
51622                 delete e.cancel; //?? why!!!
51623                 this.fireEvent("afteredit", e);
51624             }
51625         } else {
51626             this.fireEvent("afteredit", e); // always fire it!
51627         }
51628         this.view.focusCell(ed.row, ed.col);
51629     },
51630
51631     /**
51632      * Starts editing the specified for the specified row/column
51633      * @param {Number} rowIndex
51634      * @param {Number} colIndex
51635      */
51636     startEditing : function(row, col){
51637         this.stopEditing();
51638         if(this.colModel.isCellEditable(col, row)){
51639             this.view.ensureVisible(row, col, true);
51640           
51641             var r = this.dataSource.getAt(row);
51642             var field = this.colModel.getDataIndex(col);
51643             var cell = Roo.get(this.view.getCell(row,col));
51644             var e = {
51645                 grid: this,
51646                 record: r,
51647                 field: field,
51648                 value: r.data[field],
51649                 row: row,
51650                 column: col,
51651                 cancel:false 
51652             };
51653             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51654                 this.editing = true;
51655                 var ed = this.colModel.getCellEditor(col, row);
51656                 
51657                 if (!ed) {
51658                     return;
51659                 }
51660                 if(!ed.rendered){
51661                     ed.render(ed.parentEl || document.body);
51662                 }
51663                 ed.field.reset();
51664                
51665                 cell.hide();
51666                 
51667                 (function(){ // complex but required for focus issues in safari, ie and opera
51668                     ed.row = row;
51669                     ed.col = col;
51670                     ed.record = r;
51671                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51672                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51673                     this.activeEditor = ed;
51674                     var v = r.data[field];
51675                     ed.startEdit(this.view.getCell(row, col), v);
51676                     // combo's with 'displayField and name set
51677                     if (ed.field.displayField && ed.field.name) {
51678                         ed.field.el.dom.value = r.data[ed.field.name];
51679                     }
51680                     
51681                     
51682                 }).defer(50, this);
51683             }
51684         }
51685     },
51686         
51687     /**
51688      * Stops any active editing
51689      */
51690     stopEditing : function(){
51691         if(this.activeEditor){
51692             this.activeEditor.completeEdit();
51693         }
51694         this.activeEditor = null;
51695     }
51696 });/*
51697  * Based on:
51698  * Ext JS Library 1.1.1
51699  * Copyright(c) 2006-2007, Ext JS, LLC.
51700  *
51701  * Originally Released Under LGPL - original licence link has changed is not relivant.
51702  *
51703  * Fork - LGPL
51704  * <script type="text/javascript">
51705  */
51706
51707 // private - not really -- you end up using it !
51708 // This is a support class used internally by the Grid components
51709
51710 /**
51711  * @class Roo.grid.GridEditor
51712  * @extends Roo.Editor
51713  * Class for creating and editable grid elements.
51714  * @param {Object} config any settings (must include field)
51715  */
51716 Roo.grid.GridEditor = function(field, config){
51717     if (!config && field.field) {
51718         config = field;
51719         field = Roo.factory(config.field, Roo.form);
51720     }
51721     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51722     field.monitorTab = false;
51723 };
51724
51725 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51726     
51727     /**
51728      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51729      */
51730     
51731     alignment: "tl-tl",
51732     autoSize: "width",
51733     hideEl : false,
51734     cls: "x-small-editor x-grid-editor",
51735     shim:false,
51736     shadow:"frame"
51737 });/*
51738  * Based on:
51739  * Ext JS Library 1.1.1
51740  * Copyright(c) 2006-2007, Ext JS, LLC.
51741  *
51742  * Originally Released Under LGPL - original licence link has changed is not relivant.
51743  *
51744  * Fork - LGPL
51745  * <script type="text/javascript">
51746  */
51747   
51748
51749   
51750 Roo.grid.PropertyRecord = Roo.data.Record.create([
51751     {name:'name',type:'string'},  'value'
51752 ]);
51753
51754
51755 Roo.grid.PropertyStore = function(grid, source){
51756     this.grid = grid;
51757     this.store = new Roo.data.Store({
51758         recordType : Roo.grid.PropertyRecord
51759     });
51760     this.store.on('update', this.onUpdate,  this);
51761     if(source){
51762         this.setSource(source);
51763     }
51764     Roo.grid.PropertyStore.superclass.constructor.call(this);
51765 };
51766
51767
51768
51769 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51770     setSource : function(o){
51771         this.source = o;
51772         this.store.removeAll();
51773         var data = [];
51774         for(var k in o){
51775             if(this.isEditableValue(o[k])){
51776                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51777             }
51778         }
51779         this.store.loadRecords({records: data}, {}, true);
51780     },
51781
51782     onUpdate : function(ds, record, type){
51783         if(type == Roo.data.Record.EDIT){
51784             var v = record.data['value'];
51785             var oldValue = record.modified['value'];
51786             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51787                 this.source[record.id] = v;
51788                 record.commit();
51789                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51790             }else{
51791                 record.reject();
51792             }
51793         }
51794     },
51795
51796     getProperty : function(row){
51797        return this.store.getAt(row);
51798     },
51799
51800     isEditableValue: function(val){
51801         if(val && val instanceof Date){
51802             return true;
51803         }else if(typeof val == 'object' || typeof val == 'function'){
51804             return false;
51805         }
51806         return true;
51807     },
51808
51809     setValue : function(prop, value){
51810         this.source[prop] = value;
51811         this.store.getById(prop).set('value', value);
51812     },
51813
51814     getSource : function(){
51815         return this.source;
51816     }
51817 });
51818
51819 Roo.grid.PropertyColumnModel = function(grid, store){
51820     this.grid = grid;
51821     var g = Roo.grid;
51822     g.PropertyColumnModel.superclass.constructor.call(this, [
51823         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51824         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51825     ]);
51826     this.store = store;
51827     this.bselect = Roo.DomHelper.append(document.body, {
51828         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51829             {tag: 'option', value: 'true', html: 'true'},
51830             {tag: 'option', value: 'false', html: 'false'}
51831         ]
51832     });
51833     Roo.id(this.bselect);
51834     var f = Roo.form;
51835     this.editors = {
51836         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51837         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51838         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51839         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51840         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51841     };
51842     this.renderCellDelegate = this.renderCell.createDelegate(this);
51843     this.renderPropDelegate = this.renderProp.createDelegate(this);
51844 };
51845
51846 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51847     
51848     
51849     nameText : 'Name',
51850     valueText : 'Value',
51851     
51852     dateFormat : 'm/j/Y',
51853     
51854     
51855     renderDate : function(dateVal){
51856         return dateVal.dateFormat(this.dateFormat);
51857     },
51858
51859     renderBool : function(bVal){
51860         return bVal ? 'true' : 'false';
51861     },
51862
51863     isCellEditable : function(colIndex, rowIndex){
51864         return colIndex == 1;
51865     },
51866
51867     getRenderer : function(col){
51868         return col == 1 ?
51869             this.renderCellDelegate : this.renderPropDelegate;
51870     },
51871
51872     renderProp : function(v){
51873         return this.getPropertyName(v);
51874     },
51875
51876     renderCell : function(val){
51877         var rv = val;
51878         if(val instanceof Date){
51879             rv = this.renderDate(val);
51880         }else if(typeof val == 'boolean'){
51881             rv = this.renderBool(val);
51882         }
51883         return Roo.util.Format.htmlEncode(rv);
51884     },
51885
51886     getPropertyName : function(name){
51887         var pn = this.grid.propertyNames;
51888         return pn && pn[name] ? pn[name] : name;
51889     },
51890
51891     getCellEditor : function(colIndex, rowIndex){
51892         var p = this.store.getProperty(rowIndex);
51893         var n = p.data['name'], val = p.data['value'];
51894         
51895         if(typeof(this.grid.customEditors[n]) == 'string'){
51896             return this.editors[this.grid.customEditors[n]];
51897         }
51898         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51899             return this.grid.customEditors[n];
51900         }
51901         if(val instanceof Date){
51902             return this.editors['date'];
51903         }else if(typeof val == 'number'){
51904             return this.editors['number'];
51905         }else if(typeof val == 'boolean'){
51906             return this.editors['boolean'];
51907         }else{
51908             return this.editors['string'];
51909         }
51910     }
51911 });
51912
51913 /**
51914  * @class Roo.grid.PropertyGrid
51915  * @extends Roo.grid.EditorGrid
51916  * This class represents the  interface of a component based property grid control.
51917  * <br><br>Usage:<pre><code>
51918  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51919       
51920  });
51921  // set any options
51922  grid.render();
51923  * </code></pre>
51924   
51925  * @constructor
51926  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51927  * The container MUST have some type of size defined for the grid to fill. The container will be
51928  * automatically set to position relative if it isn't already.
51929  * @param {Object} config A config object that sets properties on this grid.
51930  */
51931 Roo.grid.PropertyGrid = function(container, config){
51932     config = config || {};
51933     var store = new Roo.grid.PropertyStore(this);
51934     this.store = store;
51935     var cm = new Roo.grid.PropertyColumnModel(this, store);
51936     store.store.sort('name', 'ASC');
51937     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51938         ds: store.store,
51939         cm: cm,
51940         enableColLock:false,
51941         enableColumnMove:false,
51942         stripeRows:false,
51943         trackMouseOver: false,
51944         clicksToEdit:1
51945     }, config));
51946     this.getGridEl().addClass('x-props-grid');
51947     this.lastEditRow = null;
51948     this.on('columnresize', this.onColumnResize, this);
51949     this.addEvents({
51950          /**
51951              * @event beforepropertychange
51952              * Fires before a property changes (return false to stop?)
51953              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51954              * @param {String} id Record Id
51955              * @param {String} newval New Value
51956          * @param {String} oldval Old Value
51957              */
51958         "beforepropertychange": true,
51959         /**
51960              * @event propertychange
51961              * Fires after a property changes
51962              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51963              * @param {String} id Record Id
51964              * @param {String} newval New Value
51965          * @param {String} oldval Old Value
51966              */
51967         "propertychange": true
51968     });
51969     this.customEditors = this.customEditors || {};
51970 };
51971 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51972     
51973      /**
51974      * @cfg {Object} customEditors map of colnames=> custom editors.
51975      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51976      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51977      * false disables editing of the field.
51978          */
51979     
51980       /**
51981      * @cfg {Object} propertyNames map of property Names to their displayed value
51982          */
51983     
51984     render : function(){
51985         Roo.grid.PropertyGrid.superclass.render.call(this);
51986         this.autoSize.defer(100, this);
51987     },
51988
51989     autoSize : function(){
51990         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51991         if(this.view){
51992             this.view.fitColumns();
51993         }
51994     },
51995
51996     onColumnResize : function(){
51997         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51998         this.autoSize();
51999     },
52000     /**
52001      * Sets the data for the Grid
52002      * accepts a Key => Value object of all the elements avaiable.
52003      * @param {Object} data  to appear in grid.
52004      */
52005     setSource : function(source){
52006         this.store.setSource(source);
52007         //this.autoSize();
52008     },
52009     /**
52010      * Gets all the data from the grid.
52011      * @return {Object} data  data stored in grid
52012      */
52013     getSource : function(){
52014         return this.store.getSource();
52015     }
52016 });/*
52017  * Based on:
52018  * Ext JS Library 1.1.1
52019  * Copyright(c) 2006-2007, Ext JS, LLC.
52020  *
52021  * Originally Released Under LGPL - original licence link has changed is not relivant.
52022  *
52023  * Fork - LGPL
52024  * <script type="text/javascript">
52025  */
52026  
52027 /**
52028  * @class Roo.LoadMask
52029  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52030  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52031  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52032  * element's UpdateManager load indicator and will be destroyed after the initial load.
52033  * @constructor
52034  * Create a new LoadMask
52035  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52036  * @param {Object} config The config object
52037  */
52038 Roo.LoadMask = function(el, config){
52039     this.el = Roo.get(el);
52040     Roo.apply(this, config);
52041     if(this.store){
52042         this.store.on('beforeload', this.onBeforeLoad, this);
52043         this.store.on('load', this.onLoad, this);
52044         this.store.on('loadexception', this.onLoadException, this);
52045         this.removeMask = false;
52046     }else{
52047         var um = this.el.getUpdateManager();
52048         um.showLoadIndicator = false; // disable the default indicator
52049         um.on('beforeupdate', this.onBeforeLoad, this);
52050         um.on('update', this.onLoad, this);
52051         um.on('failure', this.onLoad, this);
52052         this.removeMask = true;
52053     }
52054 };
52055
52056 Roo.LoadMask.prototype = {
52057     /**
52058      * @cfg {Boolean} removeMask
52059      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52060      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52061      */
52062     /**
52063      * @cfg {String} msg
52064      * The text to display in a centered loading message box (defaults to 'Loading...')
52065      */
52066     msg : 'Loading...',
52067     /**
52068      * @cfg {String} msgCls
52069      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52070      */
52071     msgCls : 'x-mask-loading',
52072
52073     /**
52074      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52075      * @type Boolean
52076      */
52077     disabled: false,
52078
52079     /**
52080      * Disables the mask to prevent it from being displayed
52081      */
52082     disable : function(){
52083        this.disabled = true;
52084     },
52085
52086     /**
52087      * Enables the mask so that it can be displayed
52088      */
52089     enable : function(){
52090         this.disabled = false;
52091     },
52092     
52093     onLoadException : function()
52094     {
52095         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52096             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52097         }
52098         this.el.unmask(this.removeMask);
52099     },
52100     // private
52101     onLoad : function()
52102     {
52103         this.el.unmask(this.removeMask);
52104     },
52105
52106     // private
52107     onBeforeLoad : function(){
52108         if(!this.disabled){
52109             this.el.mask(this.msg, this.msgCls);
52110         }
52111     },
52112
52113     // private
52114     destroy : function(){
52115         if(this.store){
52116             this.store.un('beforeload', this.onBeforeLoad, this);
52117             this.store.un('load', this.onLoad, this);
52118             this.store.un('loadexception', this.onLoadException, this);
52119         }else{
52120             var um = this.el.getUpdateManager();
52121             um.un('beforeupdate', this.onBeforeLoad, this);
52122             um.un('update', this.onLoad, this);
52123             um.un('failure', this.onLoad, this);
52124         }
52125     }
52126 };/*
52127  * Based on:
52128  * Ext JS Library 1.1.1
52129  * Copyright(c) 2006-2007, Ext JS, LLC.
52130  *
52131  * Originally Released Under LGPL - original licence link has changed is not relivant.
52132  *
52133  * Fork - LGPL
52134  * <script type="text/javascript">
52135  */
52136 Roo.XTemplate = function(){
52137     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52138     var s = this.html;
52139
52140     s = ['<tpl>', s, '</tpl>'].join('');
52141
52142     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52143
52144     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52145     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52146     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52147     var m, id = 0;
52148     var tpls = [];
52149
52150     while(m = s.match(re)){
52151        var m2 = m[0].match(nameRe);
52152        var m3 = m[0].match(ifRe);
52153        var m4 = m[0].match(execRe);
52154        var exp = null, fn = null, exec = null;
52155        var name = m2 && m2[1] ? m2[1] : '';
52156        if(m3){
52157            exp = m3 && m3[1] ? m3[1] : null;
52158            if(exp){
52159                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52160            }
52161        }
52162        if(m4){
52163            exp = m4 && m4[1] ? m4[1] : null;
52164            if(exp){
52165                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52166            }
52167        }
52168        if(name){
52169            switch(name){
52170                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52171                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52172                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52173            }
52174        }
52175        tpls.push({
52176             id: id,
52177             target: name,
52178             exec: exec,
52179             test: fn,
52180             body: m[1]||''
52181         });
52182        s = s.replace(m[0], '{xtpl'+ id + '}');
52183        ++id;
52184     }
52185     for(var i = tpls.length-1; i >= 0; --i){
52186         this.compileTpl(tpls[i]);
52187     }
52188     this.master = tpls[tpls.length-1];
52189     this.tpls = tpls;
52190 };
52191 Roo.extend(Roo.XTemplate, Roo.Template, {
52192
52193     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52194
52195     applySubTemplate : function(id, values, parent){
52196         var t = this.tpls[id];
52197         if(t.test && !t.test.call(this, values, parent)){
52198             return '';
52199         }
52200         if(t.exec && t.exec.call(this, values, parent)){
52201             return '';
52202         }
52203         var vs = t.target ? t.target.call(this, values, parent) : values;
52204         parent = t.target ? values : parent;
52205         if(t.target && vs instanceof Array){
52206             var buf = [];
52207             for(var i = 0, len = vs.length; i < len; i++){
52208                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52209             }
52210             return buf.join('');
52211         }
52212         return t.compiled.call(this, vs, parent);
52213     },
52214
52215     compileTpl : function(tpl){
52216         var fm = Roo.util.Format;
52217         var useF = this.disableFormats !== true;
52218         var sep = Roo.isGecko ? "+" : ",";
52219         var fn = function(m, name, format, args){
52220             if(name.substr(0, 4) == 'xtpl'){
52221                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52222             }
52223             var v;
52224             if(name.indexOf('.') != -1){
52225                 v = name;
52226             }else{
52227                 v = "values['" + name + "']";
52228             }
52229             if(format && useF){
52230                 args = args ? ',' + args : "";
52231                 if(format.substr(0, 5) != "this."){
52232                     format = "fm." + format + '(';
52233                 }else{
52234                     format = 'this.call("'+ format.substr(5) + '", ';
52235                     args = ", values";
52236                 }
52237             }else{
52238                 args= ''; format = "("+v+" === undefined ? '' : ";
52239             }
52240             return "'"+ sep + format + v + args + ")"+sep+"'";
52241         };
52242         var body;
52243         // branched to use + in gecko and [].join() in others
52244         if(Roo.isGecko){
52245             body = "tpl.compiled = function(values, parent){ return '" +
52246                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52247                     "';};";
52248         }else{
52249             body = ["tpl.compiled = function(values, parent){ return ['"];
52250             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52251             body.push("'].join('');};");
52252             body = body.join('');
52253         }
52254         /** eval:var:zzzzzzz */
52255         eval(body);
52256         return this;
52257     },
52258
52259     applyTemplate : function(values){
52260         return this.master.compiled.call(this, values, {});
52261         var s = this.subs;
52262     },
52263
52264     apply : function(){
52265         return this.applyTemplate.apply(this, arguments);
52266     },
52267
52268     compile : function(){return this;}
52269 });
52270
52271 Roo.XTemplate.from = function(el){
52272     el = Roo.getDom(el);
52273     return new Roo.XTemplate(el.value || el.innerHTML);
52274 };/*
52275  * Original code for Roojs - LGPL
52276  * <script type="text/javascript">
52277  */
52278  
52279 /**
52280  * @class Roo.XComponent
52281  * A delayed Element creator...
52282  * Or a way to group chunks of interface together.
52283  * 
52284  * Mypart.xyx = new Roo.XComponent({
52285
52286     parent : 'Mypart.xyz', // empty == document.element.!!
52287     order : '001',
52288     name : 'xxxx'
52289     region : 'xxxx'
52290     disabled : function() {} 
52291      
52292     tree : function() { // return an tree of xtype declared components
52293         var MODULE = this;
52294         return 
52295         {
52296             xtype : 'NestedLayoutPanel',
52297             // technicall
52298         }
52299      ]
52300  *})
52301  *
52302  *
52303  * It can be used to build a big heiracy, with parent etc.
52304  * or you can just use this to render a single compoent to a dom element
52305  * MYPART.render(Roo.Element | String(id) | dom_element )
52306  * 
52307  * @extends Roo.util.Observable
52308  * @constructor
52309  * @param cfg {Object} configuration of component
52310  * 
52311  */
52312 Roo.XComponent = function(cfg) {
52313     Roo.apply(this, cfg);
52314     this.addEvents({ 
52315         /**
52316              * @event built
52317              * Fires when this the componnt is built
52318              * @param {Roo.XComponent} c the component
52319              */
52320         'built' : true,
52321         /**
52322              * @event buildcomplete
52323              * Fires on the top level element when all elements have been built
52324              * @param {Roo.XComponent} c the top level component.
52325          */
52326         'buildcomplete' : true
52327         
52328     });
52329     this.region = this.region || 'center'; // default..
52330     Roo.XComponent.register(this);
52331     this.modules = false;
52332     this.el = false; // where the layout goes..
52333     
52334     
52335 }
52336 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52337     /**
52338      * @property el
52339      * The created element (with Roo.factory())
52340      * @type {Roo.Layout}
52341      */
52342     el  : false,
52343     
52344     /**
52345      * @property el
52346      * for BC  - use el in new code
52347      * @type {Roo.Layout}
52348      */
52349     panel : false,
52350     
52351     /**
52352      * @property layout
52353      * for BC  - use el in new code
52354      * @type {Roo.Layout}
52355      */
52356     layout : false,
52357     
52358      /**
52359      * @cfg {Function|boolean} disabled
52360      * If this module is disabled by some rule, return true from the funtion
52361      */
52362     disabled : false,
52363     
52364     /**
52365      * @cfg {String} parent 
52366      * Name of parent element which it get xtype added to..
52367      */
52368     parent: false,
52369     
52370     /**
52371      * @cfg {String} order
52372      * Used to set the order in which elements are created (usefull for multiple tabs)
52373      */
52374     
52375     order : false,
52376     /**
52377      * @cfg {String} name
52378      * String to display while loading.
52379      */
52380     name : false,
52381     /**
52382      * @cfg {String} region
52383      * Region to render component to (defaults to center)
52384      */
52385     region : 'center',
52386     
52387     /**
52388      * @cfg {Array} items
52389      * A single item array - the first element is the root of the tree..
52390      * It's done this way to stay compatible with the Xtype system...
52391      */
52392     items : false,
52393     
52394     
52395      /**
52396      * render
52397      * render element to dom or tree
52398      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52399      */
52400     
52401     render : function(el)
52402     {
52403         
52404         el = el || false;
52405         var hp = this.parent ? 1 : 0;
52406         
52407         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52408             // if parent is a '#.....' string, then let's use that..
52409             var ename = this.parent.substr(1)
52410             this.parent = false;
52411             el = Roo.get(ename);
52412             if (!el) {
52413                 Roo.log("Warning - element can not be found :#" + ename );
52414                 return;
52415             }
52416         }
52417         
52418         
52419         if (!this.parent) {
52420             
52421             el = el ? Roo.get(el) : false;
52422             
52423             // it's a top level one..
52424             this.parent =  {
52425                 el : new Roo.BorderLayout(el || document.body, {
52426                 
52427                      center: {
52428                          titlebar: false,
52429                          autoScroll:false,
52430                          closeOnTab: true,
52431                          tabPosition: 'top',
52432                           //resizeTabs: true,
52433                          alwaysShowTabs: el && hp? false :  true,
52434                          hideTabs: el || !hp ? true :  false,
52435                          minTabWidth: 140
52436                      }
52437                  })
52438             }
52439         }
52440         
52441         
52442             
52443         var tree = this.tree();
52444         tree.region = tree.region || this.region;
52445         this.el = this.parent.el.addxtype(tree);
52446         this.fireEvent('built', this);
52447         
52448         this.panel = this.el;
52449         this.layout = this.panel.layout;    
52450          
52451     }
52452     
52453 });
52454
52455 Roo.apply(Roo.XComponent, {
52456     
52457     /**
52458      * @property  buildCompleted
52459      * True when the builder has completed building the interface.
52460      * @type Boolean
52461      */
52462     buildCompleted : false,
52463      
52464     /**
52465      * @property  topModule
52466      * the upper most module - uses document.element as it's constructor.
52467      * @type Object
52468      */
52469      
52470     topModule  : false,
52471       
52472     /**
52473      * @property  modules
52474      * array of modules to be created by registration system.
52475      * @type {Array} of Roo.XComponent
52476      */
52477     
52478     modules : [],
52479     /**
52480      * @property  elmodules
52481      * array of modules to be created by which use #ID 
52482      * @type {Array} of Roo.XComponent
52483      */
52484      
52485     elmodules : [],
52486
52487     
52488     /**
52489      * Register components to be built later.
52490      *
52491      * This solves the following issues
52492      * - Building is not done on page load, but after an authentication process has occured.
52493      * - Interface elements are registered on page load
52494      * - Parent Interface elements may not be loaded before child, so this handles that..
52495      * 
52496      *
52497      * example:
52498      * 
52499      * MyApp.register({
52500           order : '000001',
52501           module : 'Pman.Tab.projectMgr',
52502           region : 'center',
52503           parent : 'Pman.layout',
52504           disabled : false,  // or use a function..
52505         })
52506      
52507      * * @param {Object} details about module
52508      */
52509     register : function(obj) {
52510         this.modules.push(obj);
52511          
52512     },
52513     /**
52514      * convert a string to an object..
52515      * eg. 'AAA.BBB' -> finds AAA.BBB
52516
52517      */
52518     
52519     toObject : function(str)
52520     {
52521         if (!str || typeof(str) == 'object') {
52522             return str;
52523         }
52524         if (str.substring(0,1) == '#') {
52525             return str;
52526         }
52527
52528         var ar = str.split('.');
52529         var rt, o;
52530         rt = ar.shift();
52531             /** eval:var:o */
52532         try {
52533             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52534         } catch (e) {
52535             throw "Module not found : " + str;
52536         }
52537         
52538         if (o === false) {
52539             throw "Module not found : " + str;
52540         }
52541         Roo.each(ar, function(e) {
52542             if (typeof(o[e]) == 'undefined') {
52543                 throw "Module not found : " + str;
52544             }
52545             o = o[e];
52546         });
52547         
52548         return o;
52549         
52550     },
52551     
52552     
52553     /**
52554      * move modules into their correct place in the tree..
52555      * 
52556      */
52557     preBuild : function ()
52558     {
52559         var _t = this;
52560         Roo.each(this.modules , function (obj)
52561         {
52562             var opar = obj.parent;
52563             try { 
52564                 obj.parent = this.toObject(opar);
52565             } catch(e) {
52566                 Roo.log(e.toString());
52567                 return;
52568             }
52569             
52570             if (!obj.parent) {
52571                 this.topModule = obj;
52572                 return;
52573             }
52574             if (typeof(obj.parent) == 'string') {
52575                 this.elmodules.push(obj);
52576                 return;
52577             }
52578             if (obj.parent.constructor != Roo.XComponent) {
52579                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52580             }
52581             if (!obj.parent.modules) {
52582                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52583                     function(o) { return o.order + '' }
52584                 );
52585             }
52586             
52587             obj.parent.modules.add(obj);
52588         }, this);
52589     },
52590     
52591      /**
52592      * make a list of modules to build.
52593      * @return {Array} list of modules. 
52594      */ 
52595     
52596     buildOrder : function()
52597     {
52598         var _this = this;
52599         var cmp = function(a,b) {   
52600             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52601         };
52602         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52603             throw "No top level modules to build";
52604         }
52605         
52606         // make a flat list in order of modules to build.
52607         var mods = this.topModule ? [ this.topModule ] : [];
52608         Roo.each(this.elmodules,function(e) { mods.push(e) });
52609
52610         
52611         // add modules to their parents..
52612         var addMod = function(m) {
52613            // Roo.debug && Roo.log(m.modKey);
52614             
52615             mods.push(m);
52616             if (m.modules) {
52617                 m.modules.keySort('ASC',  cmp );
52618                 m.modules.each(addMod);
52619             }
52620             // not sure if this is used any more..
52621             if (m.finalize) {
52622                 m.finalize.name = m.name + " (clean up) ";
52623                 mods.push(m.finalize);
52624             }
52625             
52626         }
52627         if (this.topModule) { 
52628             this.topModule.modules.keySort('ASC',  cmp );
52629             this.topModule.modules.each(addMod);
52630         }
52631         return mods;
52632     },
52633     
52634      /**
52635      * Build the registered modules.
52636      * @param {Object} parent element.
52637      * @param {Function} optional method to call after module has been added.
52638      * 
52639      */ 
52640    
52641     build : function() 
52642     {
52643         
52644         this.preBuild();
52645         var mods = this.buildOrder();
52646       
52647         //this.allmods = mods;
52648         //Roo.debug && Roo.log(mods);
52649         //return;
52650         if (!mods.length) { // should not happen
52651             throw "NO modules!!!";
52652         }
52653         
52654         
52655         
52656         // flash it up as modal - so we store the mask!?
52657         Roo.MessageBox.show({ title: 'loading' });
52658         Roo.MessageBox.show({
52659            title: "Please wait...",
52660            msg: "Building Interface...",
52661            width:450,
52662            progress:true,
52663            closable:false,
52664            modal: false
52665           
52666         });
52667         var total = mods.length;
52668         
52669         var _this = this;
52670         var progressRun = function() {
52671             if (!mods.length) {
52672                 Roo.debug && Roo.log('hide?');
52673                 Roo.MessageBox.hide();
52674                 if (_this.topModule) { 
52675                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52676                 }
52677                 // THE END...
52678                 return false;   
52679             }
52680             
52681             var m = mods.shift();
52682             
52683             
52684             Roo.debug && Roo.log(m);
52685             // not sure if this is supported any more.. - modules that are are just function
52686             if (typeof(m) == 'function') { 
52687                 m.call(this);
52688                 return progressRun.defer(10, _this);
52689             } 
52690             
52691             
52692             
52693             Roo.MessageBox.updateProgress(
52694                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52695                     " of " + total + 
52696                     (m.name ? (' - ' + m.name) : '')
52697                     );
52698             
52699          
52700             // is the module disabled?
52701             var disabled = (typeof(m.disabled) == 'function') ?
52702                 m.disabled.call(m.module.disabled) : m.disabled;    
52703             
52704             
52705             if (disabled) {
52706                 return progressRun(); // we do not update the display!
52707             }
52708             
52709             // now build 
52710             
52711             m.render();
52712             // it's 10 on top level, and 1 on others??? why...
52713             return progressRun.defer(10, _this);
52714              
52715         }
52716         progressRun.defer(1, _this);
52717      
52718         
52719         
52720     }
52721     
52722      
52723    
52724     
52725     
52726 });
52727  //<script type="text/javascript">
52728
52729
52730 /**
52731  * @class Roo.Login
52732  * @extends Roo.LayoutDialog
52733  * A generic Login Dialog..... - only one needed in theory!?!?
52734  *
52735  * Fires XComponent builder on success...
52736  * 
52737  * Sends 
52738  *    username,password, lang = for login actions.
52739  *    check = 1 for periodic checking that sesion is valid.
52740  *    passwordRequest = email request password
52741  *    logout = 1 = to logout
52742  * 
52743  * Affects: (this id="????" elements)
52744  *   loading  (removed) (used to indicate application is loading)
52745  *   loading-mask (hides) (used to hide application when it's building loading)
52746  *   
52747  * 
52748  * Usage: 
52749  *    
52750  * 
52751  * Myapp.login = Roo.Login({
52752      url: xxxx,
52753    
52754      realm : 'Myapp', 
52755      
52756      
52757      method : 'POST',
52758      
52759      
52760      * 
52761  })
52762  * 
52763  * 
52764  * 
52765  **/
52766  
52767 Roo.Login = function(cfg)
52768 {
52769     this.addEvents({
52770         'refreshed' : true
52771     });
52772     
52773     Roo.apply(this,cfg);
52774     
52775     Roo.onReady(function() {
52776         this.onLoad();
52777     }, this);
52778     // call parent..
52779     
52780    
52781     Roo.Login.superclass.constructor.call(this, this);
52782     //this.addxtype(this.items[0]);
52783     
52784     
52785 }
52786
52787
52788 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52789     
52790     /**
52791      * @cfg {String} method
52792      * Method used to query for login details.
52793      */
52794     
52795     method : 'POST',
52796     /**
52797      * @cfg {String} url
52798      * URL to query login data. - eg. baseURL + '/Login.php'
52799      */
52800     url : '',
52801     
52802     /**
52803      * @property user
52804      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52805      * @type {Object} 
52806      */
52807     user : false,
52808     /**
52809      * @property checkFails
52810      * Number of times we have attempted to get authentication check, and failed.
52811      * @type {Number} 
52812      */
52813     checkFails : 0,
52814       /**
52815      * @property intervalID
52816      * The window interval that does the constant login checking.
52817      * @type {Number} 
52818      */
52819     intervalID : 0,
52820     
52821     
52822     onLoad : function() // called on page load...
52823     {
52824         // load 
52825          
52826         if (Roo.get('loading')) { // clear any loading indicator..
52827             Roo.get('loading').remove();
52828         }
52829         
52830         //this.switchLang('en'); // set the language to english..
52831        
52832         this.check({
52833             success:  function(response, opts)  {  // check successfull...
52834             
52835                 var res = this.processResponse(response);
52836                 this.checkFails =0;
52837                 if (!res.success) { // error!
52838                     this.checkFails = 5;
52839                     //console.log('call failure');
52840                     return this.failure(response,opts);
52841                 }
52842                 
52843                 if (!res.data.id) { // id=0 == login failure.
52844                     return this.show();
52845                 }
52846                 
52847                               
52848                         //console.log(success);
52849                 this.fillAuth(res.data);   
52850                 this.checkFails =0;
52851                 Roo.XComponent.build();
52852             },
52853             failure : this.show
52854         });
52855         
52856     }, 
52857     
52858     
52859     check: function(cfg) // called every so often to refresh cookie etc..
52860     {
52861         if (cfg.again) { // could be undefined..
52862             this.checkFails++;
52863         } else {
52864             this.checkFails = 0;
52865         }
52866         var _this = this;
52867         if (this.sending) {
52868             if ( this.checkFails > 4) {
52869                 Roo.MessageBox.alert("Error",  
52870                     "Error getting authentication status. - try reloading, or wait a while", function() {
52871                         _this.sending = false;
52872                     }); 
52873                 return;
52874             }
52875             cfg.again = true;
52876             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52877             return;
52878         }
52879         this.sending = true;
52880         
52881         Roo.Ajax.request({  
52882             url: this.url,
52883             params: {
52884                 getAuthUser: true
52885             },  
52886             method: this.method,
52887             success:  cfg.success || this.success,
52888             failure : cfg.failure || this.failure,
52889             scope : this,
52890             callCfg : cfg
52891               
52892         });  
52893     }, 
52894     
52895     
52896     logout: function()
52897     {
52898         window.onbeforeunload = function() { }; // false does not work for IE..
52899         this.user = false;
52900         var _this = this;
52901         
52902         Roo.Ajax.request({  
52903             url: this.url,
52904             params: {
52905                 logout: 1
52906             },  
52907             method: 'GET',
52908             failure : function() {
52909                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52910                     document.location = document.location.toString() + '?ts=' + Math.random();
52911                 });
52912                 
52913             },
52914             success : function() {
52915                 _this.user = false;
52916                 this.checkFails =0;
52917                 // fixme..
52918                 document.location = document.location.toString() + '?ts=' + Math.random();
52919             }
52920               
52921               
52922         }); 
52923     },
52924     
52925     processResponse : function (response)
52926     {
52927         var res = '';
52928         try {
52929             res = Roo.decode(response.responseText);
52930             // oops...
52931             if (typeof(res) != 'object') {
52932                 res = { success : false, errorMsg : res, errors : true };
52933             }
52934             if (typeof(res.success) == 'undefined') {
52935                 res.success = false;
52936             }
52937             
52938         } catch(e) {
52939             res = { success : false,  errorMsg : response.responseText, errors : true };
52940         }
52941         return res;
52942     },
52943     
52944     success : function(response, opts)  // check successfull...
52945     {  
52946         this.sending = false;
52947         var res = this.processResponse(response);
52948         if (!res.success) {
52949             return this.failure(response, opts);
52950         }
52951         if (!res.data || !res.data.id) {
52952             return this.failure(response,opts);
52953         }
52954         //console.log(res);
52955         this.fillAuth(res.data);
52956         
52957         this.checkFails =0;
52958         
52959     },
52960     
52961     
52962     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52963     {
52964         this.authUser = -1;
52965         this.sending = false;
52966         var res = this.processResponse(response);
52967         //console.log(res);
52968         if ( this.checkFails > 2) {
52969         
52970             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52971                 "Error getting authentication status. - try reloading"); 
52972             return;
52973         }
52974         opts.callCfg.again = true;
52975         this.check.defer(1000, this, [ opts.callCfg ]);
52976         return;  
52977     },
52978     
52979     
52980     
52981     fillAuth: function(au) {
52982         this.startAuthCheck();
52983         this.authUserId = au.id;
52984         this.authUser = au;
52985         this.lastChecked = new Date();
52986         this.fireEvent('refreshed', au);
52987         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52988         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52989         au.lang = au.lang || 'en';
52990         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52991         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52992         this.switchLang(au.lang );
52993         
52994      
52995         // open system... - -on setyp..
52996         if (this.authUserId  < 0) {
52997             Roo.MessageBox.alert("Warning", 
52998                 "This is an open system - please set up a admin user with a password.");  
52999         }
53000          
53001         //Pman.onload(); // which should do nothing if it's a re-auth result...
53002         
53003              
53004     },
53005     
53006     startAuthCheck : function() // starter for timeout checking..
53007     {
53008         if (this.intervalID) { // timer already in place...
53009             return false;
53010         }
53011         var _this = this;
53012         this.intervalID =  window.setInterval(function() {
53013               _this.check(false);
53014             }, 120000); // every 120 secs = 2mins..
53015         
53016         
53017     },
53018          
53019     
53020     switchLang : function (lang) 
53021     {
53022         _T = typeof(_T) == 'undefined' ? false : _T;
53023           if (!_T || !lang.length) {
53024             return;
53025         }
53026         
53027         if (!_T && lang != 'en') {
53028             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53029             return;
53030         }
53031         
53032         if (typeof(_T.en) == 'undefined') {
53033             _T.en = {};
53034             Roo.apply(_T.en, _T);
53035         }
53036         
53037         if (typeof(_T[lang]) == 'undefined') {
53038             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53039             return;
53040         }
53041         
53042         
53043         Roo.apply(_T, _T[lang]);
53044         // just need to set the text values for everything...
53045         var _this = this;
53046         /* this will not work ...
53047         if (this.form) { 
53048             
53049                
53050             function formLabel(name, val) {
53051                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53052             }
53053             
53054             formLabel('password', "Password"+':');
53055             formLabel('username', "Email Address"+':');
53056             formLabel('lang', "Language"+':');
53057             this.dialog.setTitle("Login");
53058             this.dialog.buttons[0].setText("Forgot Password");
53059             this.dialog.buttons[1].setText("Login");
53060         }
53061         */
53062         
53063         
53064     },
53065     
53066     
53067     title: "Login",
53068     modal: true,
53069     width:  350,
53070     //height: 230,
53071     height: 180,
53072     shadow: true,
53073     minWidth:200,
53074     minHeight:180,
53075     //proxyDrag: true,
53076     closable: false,
53077     draggable: false,
53078     collapsible: false,
53079     resizable: false,
53080     center: {  // needed??
53081         autoScroll:false,
53082         titlebar: false,
53083        // tabPosition: 'top',
53084         hideTabs: true,
53085         closeOnTab: true,
53086         alwaysShowTabs: false
53087     } ,
53088     listeners : {
53089         
53090         show  : function(dlg)
53091         {
53092             //console.log(this);
53093             this.form = this.layout.getRegion('center').activePanel.form;
53094             this.form.dialog = dlg;
53095             this.buttons[0].form = this.form;
53096             this.buttons[0].dialog = dlg;
53097             this.buttons[1].form = this.form;
53098             this.buttons[1].dialog = dlg;
53099            
53100            //this.resizeToLogo.defer(1000,this);
53101             // this is all related to resizing for logos..
53102             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53103            //// if (!sz) {
53104              //   this.resizeToLogo.defer(1000,this);
53105              //   return;
53106            // }
53107             //var w = Ext.lib.Dom.getViewWidth() - 100;
53108             //var h = Ext.lib.Dom.getViewHeight() - 100;
53109             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53110             //this.center();
53111             if (this.disabled) {
53112                 this.hide();
53113                 return;
53114             }
53115             
53116             if (this.user.id < 0) { // used for inital setup situations.
53117                 return;
53118             }
53119             
53120             if (this.intervalID) {
53121                 // remove the timer
53122                 window.clearInterval(this.intervalID);
53123                 this.intervalID = false;
53124             }
53125             
53126             
53127             if (Roo.get('loading')) {
53128                 Roo.get('loading').remove();
53129             }
53130             if (Roo.get('loading-mask')) {
53131                 Roo.get('loading-mask').hide();
53132             }
53133             
53134             //incomming._node = tnode;
53135             this.form.reset();
53136             //this.dialog.modal = !modal;
53137             //this.dialog.show();
53138             this.el.unmask(); 
53139             
53140             
53141             this.form.setValues({
53142                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53143                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53144             });
53145             
53146             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53147             if (this.form.findField('username').getValue().length > 0 ){
53148                 this.form.findField('password').focus();
53149             } else {
53150                this.form.findField('username').focus();
53151             }
53152     
53153         }
53154     },
53155     items : [
53156          {
53157        
53158             xtype : 'ContentPanel',
53159             xns : Roo,
53160             region: 'center',
53161             fitToFrame : true,
53162             
53163             items : [
53164     
53165                 {
53166                
53167                     xtype : 'Form',
53168                     xns : Roo.form,
53169                     labelWidth: 100,
53170                     style : 'margin: 10px;',
53171                     
53172                     listeners : {
53173                         actionfailed : function(f, act) {
53174                             // form can return { errors: .... }
53175                                 
53176                             //act.result.errors // invalid form element list...
53177                             //act.result.errorMsg// invalid form element list...
53178                             
53179                             this.dialog.el.unmask();
53180                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53181                                         "Login failed - communication error - try again.");
53182                                       
53183                         },
53184                         actioncomplete: function(re, act) {
53185                              
53186                             Roo.state.Manager.set(
53187                                 this.dialog.realm + '.username',  
53188                                     this.findField('username').getValue()
53189                             );
53190                             Roo.state.Manager.set(
53191                                 this.dialog.realm + '.lang',  
53192                                 this.findField('lang').getValue() 
53193                             );
53194                             
53195                             this.dialog.fillAuth(act.result.data);
53196                               
53197                             this.dialog.hide();
53198                             
53199                             if (Roo.get('loading-mask')) {
53200                                 Roo.get('loading-mask').show();
53201                             }
53202                             Roo.XComponent.build();
53203                             
53204                              
53205                             
53206                         }
53207                     },
53208                     items : [
53209                         {
53210                             xtype : 'TextField',
53211                             xns : Roo.form,
53212                             fieldLabel: "Email Address",
53213                             name: 'username',
53214                             width:200,
53215                             autoCreate : {tag: "input", type: "text", size: "20"}
53216                         },
53217                         {
53218                             xtype : 'TextField',
53219                             xns : Roo.form,
53220                             fieldLabel: "Password",
53221                             inputType: 'password',
53222                             name: 'password',
53223                             width:200,
53224                             autoCreate : {tag: "input", type: "text", size: "20"},
53225                             listeners : {
53226                                 specialkey : function(e,ev) {
53227                                     if (ev.keyCode == 13) {
53228                                         this.form.dialog.el.mask("Logging in");
53229                                         this.form.doAction('submit', {
53230                                             url: this.form.dialog.url,
53231                                             method: this.form.dialog.method
53232                                         });
53233                                     }
53234                                 }
53235                             }  
53236                         },
53237                         {
53238                             xtype : 'ComboBox',
53239                             xns : Roo.form,
53240                             fieldLabel: "Language",
53241                             name : 'langdisp',
53242                             store: {
53243                                 xtype : 'SimpleStore',
53244                                 fields: ['lang', 'ldisp'],
53245                                 data : [
53246                                     [ 'en', 'English' ],
53247                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53248                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53249                                 ]
53250                             },
53251                             
53252                             valueField : 'lang',
53253                             hiddenName:  'lang',
53254                             width: 200,
53255                             displayField:'ldisp',
53256                             typeAhead: false,
53257                             editable: false,
53258                             mode: 'local',
53259                             triggerAction: 'all',
53260                             emptyText:'Select a Language...',
53261                             selectOnFocus:true,
53262                             listeners : {
53263                                 select :  function(cb, rec, ix) {
53264                                     this.form.switchLang(rec.data.lang);
53265                                 }
53266                             }
53267                         
53268                         }
53269                     ]
53270                 }
53271                   
53272                 
53273             ]
53274         }
53275     ],
53276     buttons : [
53277         {
53278             xtype : 'Button',
53279             xns : 'Roo',
53280             text : "Forgot Password",
53281             listeners : {
53282                 click : function() {
53283                     //console.log(this);
53284                     var n = this.form.findField('username').getValue();
53285                     if (!n.length) {
53286                         Roo.MessageBox.alert("Error", "Fill in your email address");
53287                         return;
53288                     }
53289                     Roo.Ajax.request({
53290                         url: this.dialog.url,
53291                         params: {
53292                             passwordRequest: n
53293                         },
53294                         method: this.dialog.method,
53295                         success:  function(response, opts)  {  // check successfull...
53296                         
53297                             var res = this.dialog.processResponse(response);
53298                             if (!res.success) { // error!
53299                                Roo.MessageBox.alert("Error" ,
53300                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53301                                return;
53302                             }
53303                             Roo.MessageBox.alert("Notice" ,
53304                                 "Please check you email for the Password Reset message");
53305                         },
53306                         failure : function() {
53307                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53308                         }
53309                         
53310                     });
53311                 }
53312             }
53313         },
53314         {
53315             xtype : 'Button',
53316             xns : 'Roo',
53317             text : "Login",
53318             listeners : {
53319                 
53320                 click : function () {
53321                         
53322                     this.dialog.el.mask("Logging in");
53323                     this.form.doAction('submit', {
53324                             url: this.dialog.url,
53325                             method: this.dialog.method
53326                     });
53327                 }
53328             }
53329         }
53330     ]
53331   
53332   
53333 })
53334  
53335
53336
53337