roojs-ui-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
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{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 Roo.lib.Dom = {
1778     getViewWidth : function(full) {
1779         return full ? this.getDocumentWidth() : this.getViewportWidth();
1780     },
1781
1782     getViewHeight : function(full) {
1783         return full ? this.getDocumentHeight() : this.getViewportHeight();
1784     },
1785
1786     getDocumentHeight: function() {
1787         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1788         return Math.max(scrollHeight, this.getViewportHeight());
1789     },
1790
1791     getDocumentWidth: function() {
1792         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1793         return Math.max(scrollWidth, this.getViewportWidth());
1794     },
1795
1796     getViewportHeight: function() {
1797         var height = self.innerHeight;
1798         var mode = document.compatMode;
1799
1800         if ((mode || Roo.isIE) && !Roo.isOpera) {
1801             height = (mode == "CSS1Compat") ?
1802                      document.documentElement.clientHeight :
1803                      document.body.clientHeight;
1804         }
1805
1806         return height;
1807     },
1808
1809     getViewportWidth: function() {
1810         var width = self.innerWidth;
1811         var mode = document.compatMode;
1812
1813         if (mode || Roo.isIE) {
1814             width = (mode == "CSS1Compat") ?
1815                     document.documentElement.clientWidth :
1816                     document.body.clientWidth;
1817         }
1818         return width;
1819     },
1820
1821     isAncestor : function(p, c) {
1822         p = Roo.getDom(p);
1823         c = Roo.getDom(c);
1824         if (!p || !c) {
1825             return false;
1826         }
1827
1828         if (p.contains && !Roo.isSafari) {
1829             return p.contains(c);
1830         } else if (p.compareDocumentPosition) {
1831             return !!(p.compareDocumentPosition(c) & 16);
1832         } else {
1833             var parent = c.parentNode;
1834             while (parent) {
1835                 if (parent == p) {
1836                     return true;
1837                 }
1838                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1839                     return false;
1840                 }
1841                 parent = parent.parentNode;
1842             }
1843             return false;
1844         }
1845     },
1846
1847     getRegion : function(el) {
1848         return Roo.lib.Region.getRegion(el);
1849     },
1850
1851     getY : function(el) {
1852         return this.getXY(el)[1];
1853     },
1854
1855     getX : function(el) {
1856         return this.getXY(el)[0];
1857     },
1858
1859     getXY : function(el) {
1860         var p, pe, b, scroll, bd = document.body;
1861         el = Roo.getDom(el);
1862         var fly = Roo.lib.AnimBase.fly;
1863         if (el.getBoundingClientRect) {
1864             b = el.getBoundingClientRect();
1865             scroll = fly(document).getScroll();
1866             return [b.left + scroll.left, b.top + scroll.top];
1867         }
1868         var x = 0, y = 0;
1869
1870         p = el;
1871
1872         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1873
1874         while (p) {
1875
1876             x += p.offsetLeft;
1877             y += p.offsetTop;
1878
1879             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1880                 hasAbsolute = true;
1881             }
1882
1883             if (Roo.isGecko) {
1884                 pe = fly(p);
1885
1886                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1887                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1888
1889
1890                 x += bl;
1891                 y += bt;
1892
1893
1894                 if (p != el && pe.getStyle('overflow') != 'visible') {
1895                     x += bl;
1896                     y += bt;
1897                 }
1898             }
1899             p = p.offsetParent;
1900         }
1901
1902         if (Roo.isSafari && hasAbsolute) {
1903             x -= bd.offsetLeft;
1904             y -= bd.offsetTop;
1905         }
1906
1907         if (Roo.isGecko && !hasAbsolute) {
1908             var dbd = fly(bd);
1909             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1910             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1911         }
1912
1913         p = el.parentNode;
1914         while (p && p != bd) {
1915             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1916                 x -= p.scrollLeft;
1917                 y -= p.scrollTop;
1918             }
1919             p = p.parentNode;
1920         }
1921         return [x, y];
1922     },
1923  
1924   
1925
1926
1927     setXY : function(el, xy) {
1928         el = Roo.fly(el, '_setXY');
1929         el.position();
1930         var pts = el.translatePoints(xy);
1931         if (xy[0] !== false) {
1932             el.dom.style.left = pts.left + "px";
1933         }
1934         if (xy[1] !== false) {
1935             el.dom.style.top = pts.top + "px";
1936         }
1937     },
1938
1939     setX : function(el, x) {
1940         this.setXY(el, [x, false]);
1941     },
1942
1943     setY : function(el, y) {
1944         this.setXY(el, [false, y]);
1945     }
1946 };
1947 /*
1948  * Portions of this file are based on pieces of Yahoo User Interface Library
1949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1950  * YUI licensed under the BSD License:
1951  * http://developer.yahoo.net/yui/license.txt
1952  * <script type="text/javascript">
1953  *
1954  */
1955
1956 Roo.lib.Event = function() {
1957     var loadComplete = false;
1958     var listeners = [];
1959     var unloadListeners = [];
1960     var retryCount = 0;
1961     var onAvailStack = [];
1962     var counter = 0;
1963     var lastError = null;
1964
1965     return {
1966         POLL_RETRYS: 200,
1967         POLL_INTERVAL: 20,
1968         EL: 0,
1969         TYPE: 1,
1970         FN: 2,
1971         WFN: 3,
1972         OBJ: 3,
1973         ADJ_SCOPE: 4,
1974         _interval: null,
1975
1976         startInterval: function() {
1977             if (!this._interval) {
1978                 var self = this;
1979                 var callback = function() {
1980                     self._tryPreloadAttach();
1981                 };
1982                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1983
1984             }
1985         },
1986
1987         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1988             onAvailStack.push({ id:         p_id,
1989                 fn:         p_fn,
1990                 obj:        p_obj,
1991                 override:   p_override,
1992                 checkReady: false    });
1993
1994             retryCount = this.POLL_RETRYS;
1995             this.startInterval();
1996         },
1997
1998
1999         addListener: function(el, eventName, fn) {
2000             el = Roo.getDom(el);
2001             if (!el || !fn) {
2002                 return false;
2003             }
2004
2005             if ("unload" == eventName) {
2006                 unloadListeners[unloadListeners.length] =
2007                 [el, eventName, fn];
2008                 return true;
2009             }
2010
2011             var wrappedFn = function(e) {
2012                 return fn(Roo.lib.Event.getEvent(e));
2013             };
2014
2015             var li = [el, eventName, fn, wrappedFn];
2016
2017             var index = listeners.length;
2018             listeners[index] = li;
2019
2020             this.doAdd(el, eventName, wrappedFn, false);
2021             return true;
2022
2023         },
2024
2025
2026         removeListener: function(el, eventName, fn) {
2027             var i, len;
2028
2029             el = Roo.getDom(el);
2030
2031             if(!fn) {
2032                 return this.purgeElement(el, false, eventName);
2033             }
2034
2035
2036             if ("unload" == eventName) {
2037
2038                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2039                     var li = unloadListeners[i];
2040                     if (li &&
2041                         li[0] == el &&
2042                         li[1] == eventName &&
2043                         li[2] == fn) {
2044                         unloadListeners.splice(i, 1);
2045                         return true;
2046                     }
2047                 }
2048
2049                 return false;
2050             }
2051
2052             var cacheItem = null;
2053
2054
2055             var index = arguments[3];
2056
2057             if ("undefined" == typeof index) {
2058                 index = this._getCacheIndex(el, eventName, fn);
2059             }
2060
2061             if (index >= 0) {
2062                 cacheItem = listeners[index];
2063             }
2064
2065             if (!el || !cacheItem) {
2066                 return false;
2067             }
2068
2069             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2070
2071             delete listeners[index][this.WFN];
2072             delete listeners[index][this.FN];
2073             listeners.splice(index, 1);
2074
2075             return true;
2076
2077         },
2078
2079
2080         getTarget: function(ev, resolveTextNode) {
2081             ev = ev.browserEvent || ev;
2082             var t = ev.target || ev.srcElement;
2083             return this.resolveTextNode(t);
2084         },
2085
2086
2087         resolveTextNode: function(node) {
2088             if (Roo.isSafari && node && 3 == node.nodeType) {
2089                 return node.parentNode;
2090             } else {
2091                 return node;
2092             }
2093         },
2094
2095
2096         getPageX: function(ev) {
2097             ev = ev.browserEvent || ev;
2098             var x = ev.pageX;
2099             if (!x && 0 !== x) {
2100                 x = ev.clientX || 0;
2101
2102                 if (Roo.isIE) {
2103                     x += this.getScroll()[1];
2104                 }
2105             }
2106
2107             return x;
2108         },
2109
2110
2111         getPageY: function(ev) {
2112             ev = ev.browserEvent || ev;
2113             var y = ev.pageY;
2114             if (!y && 0 !== y) {
2115                 y = ev.clientY || 0;
2116
2117                 if (Roo.isIE) {
2118                     y += this.getScroll()[0];
2119                 }
2120             }
2121
2122
2123             return y;
2124         },
2125
2126
2127         getXY: function(ev) {
2128             ev = ev.browserEvent || ev;
2129             return [this.getPageX(ev), this.getPageY(ev)];
2130         },
2131
2132
2133         getRelatedTarget: function(ev) {
2134             ev = ev.browserEvent || ev;
2135             var t = ev.relatedTarget;
2136             if (!t) {
2137                 if (ev.type == "mouseout") {
2138                     t = ev.toElement;
2139                 } else if (ev.type == "mouseover") {
2140                     t = ev.fromElement;
2141                 }
2142             }
2143
2144             return this.resolveTextNode(t);
2145         },
2146
2147
2148         getTime: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             if (!ev.time) {
2151                 var t = new Date().getTime();
2152                 try {
2153                     ev.time = t;
2154                 } catch(ex) {
2155                     this.lastError = ex;
2156                     return t;
2157                 }
2158             }
2159
2160             return ev.time;
2161         },
2162
2163
2164         stopEvent: function(ev) {
2165             this.stopPropagation(ev);
2166             this.preventDefault(ev);
2167         },
2168
2169
2170         stopPropagation: function(ev) {
2171             ev = ev.browserEvent || ev;
2172             if (ev.stopPropagation) {
2173                 ev.stopPropagation();
2174             } else {
2175                 ev.cancelBubble = true;
2176             }
2177         },
2178
2179
2180         preventDefault: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             if(ev.preventDefault) {
2183                 ev.preventDefault();
2184             } else {
2185                 ev.returnValue = false;
2186             }
2187         },
2188
2189
2190         getEvent: function(e) {
2191             var ev = e || window.event;
2192             if (!ev) {
2193                 var c = this.getEvent.caller;
2194                 while (c) {
2195                     ev = c.arguments[0];
2196                     if (ev && Event == ev.constructor) {
2197                         break;
2198                     }
2199                     c = c.caller;
2200                 }
2201             }
2202             return ev;
2203         },
2204
2205
2206         getCharCode: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             return ev.charCode || ev.keyCode || 0;
2209         },
2210
2211
2212         _getCacheIndex: function(el, eventName, fn) {
2213             for (var i = 0,len = listeners.length; i < len; ++i) {
2214                 var li = listeners[i];
2215                 if (li &&
2216                     li[this.FN] == fn &&
2217                     li[this.EL] == el &&
2218                     li[this.TYPE] == eventName) {
2219                     return i;
2220                 }
2221             }
2222
2223             return -1;
2224         },
2225
2226
2227         elCache: {},
2228
2229
2230         getEl: function(id) {
2231             return document.getElementById(id);
2232         },
2233
2234
2235         clearCache: function() {
2236         },
2237
2238
2239         _load: function(e) {
2240             loadComplete = true;
2241             var EU = Roo.lib.Event;
2242
2243
2244             if (Roo.isIE) {
2245                 EU.doRemove(window, "load", EU._load);
2246             }
2247         },
2248
2249
2250         _tryPreloadAttach: function() {
2251
2252             if (this.locked) {
2253                 return false;
2254             }
2255
2256             this.locked = true;
2257
2258
2259             var tryAgain = !loadComplete;
2260             if (!tryAgain) {
2261                 tryAgain = (retryCount > 0);
2262             }
2263
2264
2265             var notAvail = [];
2266             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2267                 var item = onAvailStack[i];
2268                 if (item) {
2269                     var el = this.getEl(item.id);
2270
2271                     if (el) {
2272                         if (!item.checkReady ||
2273                             loadComplete ||
2274                             el.nextSibling ||
2275                             (document && document.body)) {
2276
2277                             var scope = el;
2278                             if (item.override) {
2279                                 if (item.override === true) {
2280                                     scope = item.obj;
2281                                 } else {
2282                                     scope = item.override;
2283                                 }
2284                             }
2285                             item.fn.call(scope, item.obj);
2286                             onAvailStack[i] = null;
2287                         }
2288                     } else {
2289                         notAvail.push(item);
2290                     }
2291                 }
2292             }
2293
2294             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2295
2296             if (tryAgain) {
2297
2298                 this.startInterval();
2299             } else {
2300                 clearInterval(this._interval);
2301                 this._interval = null;
2302             }
2303
2304             this.locked = false;
2305
2306             return true;
2307
2308         },
2309
2310
2311         purgeElement: function(el, recurse, eventName) {
2312             var elListeners = this.getListeners(el, eventName);
2313             if (elListeners) {
2314                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2315                     var l = elListeners[i];
2316                     this.removeListener(el, l.type, l.fn);
2317                 }
2318             }
2319
2320             if (recurse && el && el.childNodes) {
2321                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2322                     this.purgeElement(el.childNodes[i], recurse, eventName);
2323                 }
2324             }
2325         },
2326
2327
2328         getListeners: function(el, eventName) {
2329             var results = [], searchLists;
2330             if (!eventName) {
2331                 searchLists = [listeners, unloadListeners];
2332             } else if (eventName == "unload") {
2333                 searchLists = [unloadListeners];
2334             } else {
2335                 searchLists = [listeners];
2336             }
2337
2338             for (var j = 0; j < searchLists.length; ++j) {
2339                 var searchList = searchLists[j];
2340                 if (searchList && searchList.length > 0) {
2341                     for (var i = 0,len = searchList.length; i < len; ++i) {
2342                         var l = searchList[i];
2343                         if (l && l[this.EL] === el &&
2344                             (!eventName || eventName === l[this.TYPE])) {
2345                             results.push({
2346                                 type:   l[this.TYPE],
2347                                 fn:     l[this.FN],
2348                                 obj:    l[this.OBJ],
2349                                 adjust: l[this.ADJ_SCOPE],
2350                                 index:  i
2351                             });
2352                         }
2353                     }
2354                 }
2355             }
2356
2357             return (results.length) ? results : null;
2358         },
2359
2360
2361         _unload: function(e) {
2362
2363             var EU = Roo.lib.Event, i, j, l, len, index;
2364
2365             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2366                 l = unloadListeners[i];
2367                 if (l) {
2368                     var scope = window;
2369                     if (l[EU.ADJ_SCOPE]) {
2370                         if (l[EU.ADJ_SCOPE] === true) {
2371                             scope = l[EU.OBJ];
2372                         } else {
2373                             scope = l[EU.ADJ_SCOPE];
2374                         }
2375                     }
2376                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2377                     unloadListeners[i] = null;
2378                     l = null;
2379                     scope = null;
2380                 }
2381             }
2382
2383             unloadListeners = null;
2384
2385             if (listeners && listeners.length > 0) {
2386                 j = listeners.length;
2387                 while (j) {
2388                     index = j - 1;
2389                     l = listeners[index];
2390                     if (l) {
2391                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2392                                 l[EU.FN], index);
2393                     }
2394                     j = j - 1;
2395                 }
2396                 l = null;
2397
2398                 EU.clearCache();
2399             }
2400
2401             EU.doRemove(window, "unload", EU._unload);
2402
2403         },
2404
2405
2406         getScroll: function() {
2407             var dd = document.documentElement, db = document.body;
2408             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2409                 return [dd.scrollTop, dd.scrollLeft];
2410             } else if (db) {
2411                 return [db.scrollTop, db.scrollLeft];
2412             } else {
2413                 return [0, 0];
2414             }
2415         },
2416
2417
2418         doAdd: function () {
2419             if (window.addEventListener) {
2420                 return function(el, eventName, fn, capture) {
2421                     el.addEventListener(eventName, fn, (capture));
2422                 };
2423             } else if (window.attachEvent) {
2424                 return function(el, eventName, fn, capture) {
2425                     el.attachEvent("on" + eventName, fn);
2426                 };
2427             } else {
2428                 return function() {
2429                 };
2430             }
2431         }(),
2432
2433
2434         doRemove: function() {
2435             if (window.removeEventListener) {
2436                 return function (el, eventName, fn, capture) {
2437                     el.removeEventListener(eventName, fn, (capture));
2438                 };
2439             } else if (window.detachEvent) {
2440                 return function (el, eventName, fn) {
2441                     el.detachEvent("on" + eventName, fn);
2442                 };
2443             } else {
2444                 return function() {
2445                 };
2446             }
2447         }()
2448     };
2449     
2450 }();
2451 (function() {     
2452    
2453     var E = Roo.lib.Event;
2454     E.on = E.addListener;
2455     E.un = E.removeListener;
2456
2457     if (document && document.body) {
2458         E._load();
2459     } else {
2460         E.doAdd(window, "load", E._load);
2461     }
2462     E.doAdd(window, "unload", E._unload);
2463     E._tryPreloadAttach();
2464 })();
2465
2466 /*
2467  * Portions of this file are based on pieces of Yahoo User Interface Library
2468  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2469  * YUI licensed under the BSD License:
2470  * http://developer.yahoo.net/yui/license.txt
2471  * <script type="text/javascript">
2472  *
2473  */
2474
2475 (function() {
2476     /**
2477      * @class Roo.lib.Ajax
2478      *
2479      */
2480     Roo.lib.Ajax = {
2481         /**
2482          * @static 
2483          */
2484         request : function(method, uri, cb, data, options) {
2485             if(options){
2486                 var hs = options.headers;
2487                 if(hs){
2488                     for(var h in hs){
2489                         if(hs.hasOwnProperty(h)){
2490                             this.initHeader(h, hs[h], false);
2491                         }
2492                     }
2493                 }
2494                 if(options.xmlData){
2495                     this.initHeader('Content-Type', 'text/xml', false);
2496                     method = 'POST';
2497                     data = options.xmlData;
2498                 }
2499             }
2500
2501             return this.asyncRequest(method, uri, cb, data);
2502         },
2503
2504         serializeForm : function(form) {
2505             if(typeof form == 'string') {
2506                 form = (document.getElementById(form) || document.forms[form]);
2507             }
2508
2509             var el, name, val, disabled, data = '', hasSubmit = false;
2510             for (var i = 0; i < form.elements.length; i++) {
2511                 el = form.elements[i];
2512                 disabled = form.elements[i].disabled;
2513                 name = form.elements[i].name;
2514                 val = form.elements[i].value;
2515
2516                 if (!disabled && name){
2517                     switch (el.type)
2518                             {
2519                         case 'select-one':
2520                         case 'select-multiple':
2521                             for (var j = 0; j < el.options.length; j++) {
2522                                 if (el.options[j].selected) {
2523                                     if (Roo.isIE) {
2524                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2525                                     }
2526                                     else {
2527                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2528                                     }
2529                                 }
2530                             }
2531                             break;
2532                         case 'radio':
2533                         case 'checkbox':
2534                             if (el.checked) {
2535                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2536                             }
2537                             break;
2538                         case 'file':
2539
2540                         case undefined:
2541
2542                         case 'reset':
2543
2544                         case 'button':
2545
2546                             break;
2547                         case 'submit':
2548                             if(hasSubmit == false) {
2549                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2550                                 hasSubmit = true;
2551                             }
2552                             break;
2553                         default:
2554                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2555                             break;
2556                     }
2557                 }
2558             }
2559             data = data.substr(0, data.length - 1);
2560             return data;
2561         },
2562
2563         headers:{},
2564
2565         hasHeaders:false,
2566
2567         useDefaultHeader:true,
2568
2569         defaultPostHeader:'application/x-www-form-urlencoded',
2570
2571         useDefaultXhrHeader:true,
2572
2573         defaultXhrHeader:'XMLHttpRequest',
2574
2575         hasDefaultHeaders:true,
2576
2577         defaultHeaders:{},
2578
2579         poll:{},
2580
2581         timeout:{},
2582
2583         pollInterval:50,
2584
2585         transactionId:0,
2586
2587         setProgId:function(id)
2588         {
2589             this.activeX.unshift(id);
2590         },
2591
2592         setDefaultPostHeader:function(b)
2593         {
2594             this.useDefaultHeader = b;
2595         },
2596
2597         setDefaultXhrHeader:function(b)
2598         {
2599             this.useDefaultXhrHeader = b;
2600         },
2601
2602         setPollingInterval:function(i)
2603         {
2604             if (typeof i == 'number' && isFinite(i)) {
2605                 this.pollInterval = i;
2606             }
2607         },
2608
2609         createXhrObject:function(transactionId)
2610         {
2611             var obj,http;
2612             try
2613             {
2614
2615                 http = new XMLHttpRequest();
2616
2617                 obj = { conn:http, tId:transactionId };
2618             }
2619             catch(e)
2620             {
2621                 for (var i = 0; i < this.activeX.length; ++i) {
2622                     try
2623                     {
2624
2625                         http = new ActiveXObject(this.activeX[i]);
2626
2627                         obj = { conn:http, tId:transactionId };
2628                         break;
2629                     }
2630                     catch(e) {
2631                     }
2632                 }
2633             }
2634             finally
2635             {
2636                 return obj;
2637             }
2638         },
2639
2640         getConnectionObject:function()
2641         {
2642             var o;
2643             var tId = this.transactionId;
2644
2645             try
2646             {
2647                 o = this.createXhrObject(tId);
2648                 if (o) {
2649                     this.transactionId++;
2650                 }
2651             }
2652             catch(e) {
2653             }
2654             finally
2655             {
2656                 return o;
2657             }
2658         },
2659
2660         asyncRequest:function(method, uri, callback, postData)
2661         {
2662             var o = this.getConnectionObject();
2663
2664             if (!o) {
2665                 return null;
2666             }
2667             else {
2668                 o.conn.open(method, uri, true);
2669
2670                 if (this.useDefaultXhrHeader) {
2671                     if (!this.defaultHeaders['X-Requested-With']) {
2672                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2673                     }
2674                 }
2675
2676                 if(postData && this.useDefaultHeader){
2677                     this.initHeader('Content-Type', this.defaultPostHeader);
2678                 }
2679
2680                  if (this.hasDefaultHeaders || this.hasHeaders) {
2681                     this.setHeader(o);
2682                 }
2683
2684                 this.handleReadyState(o, callback);
2685                 o.conn.send(postData || null);
2686
2687                 return o;
2688             }
2689         },
2690
2691         handleReadyState:function(o, callback)
2692         {
2693             var oConn = this;
2694
2695             if (callback && callback.timeout) {
2696                 this.timeout[o.tId] = window.setTimeout(function() {
2697                     oConn.abort(o, callback, true);
2698                 }, callback.timeout);
2699             }
2700
2701             this.poll[o.tId] = window.setInterval(
2702                     function() {
2703                         if (o.conn && o.conn.readyState == 4) {
2704                             window.clearInterval(oConn.poll[o.tId]);
2705                             delete oConn.poll[o.tId];
2706
2707                             if(callback && callback.timeout) {
2708                                 window.clearTimeout(oConn.timeout[o.tId]);
2709                                 delete oConn.timeout[o.tId];
2710                             }
2711
2712                             oConn.handleTransactionResponse(o, callback);
2713                         }
2714                     }
2715                     , this.pollInterval);
2716         },
2717
2718         handleTransactionResponse:function(o, callback, isAbort)
2719         {
2720
2721             if (!callback) {
2722                 this.releaseObject(o);
2723                 return;
2724             }
2725
2726             var httpStatus, responseObject;
2727
2728             try
2729             {
2730                 if (o.conn.status !== undefined && o.conn.status != 0) {
2731                     httpStatus = o.conn.status;
2732                 }
2733                 else {
2734                     httpStatus = 13030;
2735                 }
2736             }
2737             catch(e) {
2738
2739
2740                 httpStatus = 13030;
2741             }
2742
2743             if (httpStatus >= 200 && httpStatus < 300) {
2744                 responseObject = this.createResponseObject(o, callback.argument);
2745                 if (callback.success) {
2746                     if (!callback.scope) {
2747                         callback.success(responseObject);
2748                     }
2749                     else {
2750
2751
2752                         callback.success.apply(callback.scope, [responseObject]);
2753                     }
2754                 }
2755             }
2756             else {
2757                 switch (httpStatus) {
2758
2759                     case 12002:
2760                     case 12029:
2761                     case 12030:
2762                     case 12031:
2763                     case 12152:
2764                     case 13030:
2765                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2766                         if (callback.failure) {
2767                             if (!callback.scope) {
2768                                 callback.failure(responseObject);
2769                             }
2770                             else {
2771                                 callback.failure.apply(callback.scope, [responseObject]);
2772                             }
2773                         }
2774                         break;
2775                     default:
2776                         responseObject = this.createResponseObject(o, callback.argument);
2777                         if (callback.failure) {
2778                             if (!callback.scope) {
2779                                 callback.failure(responseObject);
2780                             }
2781                             else {
2782                                 callback.failure.apply(callback.scope, [responseObject]);
2783                             }
2784                         }
2785                 }
2786             }
2787
2788             this.releaseObject(o);
2789             responseObject = null;
2790         },
2791
2792         createResponseObject:function(o, callbackArg)
2793         {
2794             var obj = {};
2795             var headerObj = {};
2796
2797             try
2798             {
2799                 var headerStr = o.conn.getAllResponseHeaders();
2800                 var header = headerStr.split('\n');
2801                 for (var i = 0; i < header.length; i++) {
2802                     var delimitPos = header[i].indexOf(':');
2803                     if (delimitPos != -1) {
2804                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2805                     }
2806                 }
2807             }
2808             catch(e) {
2809             }
2810
2811             obj.tId = o.tId;
2812             obj.status = o.conn.status;
2813             obj.statusText = o.conn.statusText;
2814             obj.getResponseHeader = headerObj;
2815             obj.getAllResponseHeaders = headerStr;
2816             obj.responseText = o.conn.responseText;
2817             obj.responseXML = o.conn.responseXML;
2818
2819             if (typeof callbackArg !== undefined) {
2820                 obj.argument = callbackArg;
2821             }
2822
2823             return obj;
2824         },
2825
2826         createExceptionObject:function(tId, callbackArg, isAbort)
2827         {
2828             var COMM_CODE = 0;
2829             var COMM_ERROR = 'communication failure';
2830             var ABORT_CODE = -1;
2831             var ABORT_ERROR = 'transaction aborted';
2832
2833             var obj = {};
2834
2835             obj.tId = tId;
2836             if (isAbort) {
2837                 obj.status = ABORT_CODE;
2838                 obj.statusText = ABORT_ERROR;
2839             }
2840             else {
2841                 obj.status = COMM_CODE;
2842                 obj.statusText = COMM_ERROR;
2843             }
2844
2845             if (callbackArg) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         initHeader:function(label, value, isDefault)
2853         {
2854             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2855
2856             if (headerObj[label] === undefined) {
2857                 headerObj[label] = value;
2858             }
2859             else {
2860
2861
2862                 headerObj[label] = value + "," + headerObj[label];
2863             }
2864
2865             if (isDefault) {
2866                 this.hasDefaultHeaders = true;
2867             }
2868             else {
2869                 this.hasHeaders = true;
2870             }
2871         },
2872
2873
2874         setHeader:function(o)
2875         {
2876             if (this.hasDefaultHeaders) {
2877                 for (var prop in this.defaultHeaders) {
2878                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2879                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2880                     }
2881                 }
2882             }
2883
2884             if (this.hasHeaders) {
2885                 for (var prop in this.headers) {
2886                     if (this.headers.hasOwnProperty(prop)) {
2887                         o.conn.setRequestHeader(prop, this.headers[prop]);
2888                     }
2889                 }
2890                 this.headers = {};
2891                 this.hasHeaders = false;
2892             }
2893         },
2894
2895         resetDefaultHeaders:function() {
2896             delete this.defaultHeaders;
2897             this.defaultHeaders = {};
2898             this.hasDefaultHeaders = false;
2899         },
2900
2901         abort:function(o, callback, isTimeout)
2902         {
2903             if(this.isCallInProgress(o)) {
2904                 o.conn.abort();
2905                 window.clearInterval(this.poll[o.tId]);
2906                 delete this.poll[o.tId];
2907                 if (isTimeout) {
2908                     delete this.timeout[o.tId];
2909                 }
2910
2911                 this.handleTransactionResponse(o, callback, true);
2912
2913                 return true;
2914             }
2915             else {
2916                 return false;
2917             }
2918         },
2919
2920
2921         isCallInProgress:function(o)
2922         {
2923             if (o && o.conn) {
2924                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2925             }
2926             else {
2927
2928                 return false;
2929             }
2930         },
2931
2932
2933         releaseObject:function(o)
2934         {
2935
2936             o.conn = null;
2937
2938             o = null;
2939         },
2940
2941         activeX:[
2942         'MSXML2.XMLHTTP.3.0',
2943         'MSXML2.XMLHTTP',
2944         'Microsoft.XMLHTTP'
2945         ]
2946
2947
2948     };
2949 })();/*
2950  * Portions of this file are based on pieces of Yahoo User Interface Library
2951  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2952  * YUI licensed under the BSD License:
2953  * http://developer.yahoo.net/yui/license.txt
2954  * <script type="text/javascript">
2955  *
2956  */
2957
2958 Roo.lib.Region = function(t, r, b, l) {
2959     this.top = t;
2960     this[1] = t;
2961     this.right = r;
2962     this.bottom = b;
2963     this.left = l;
2964     this[0] = l;
2965 };
2966
2967
2968 Roo.lib.Region.prototype = {
2969     contains : function(region) {
2970         return ( region.left >= this.left &&
2971                  region.right <= this.right &&
2972                  region.top >= this.top &&
2973                  region.bottom <= this.bottom    );
2974
2975     },
2976
2977     getArea : function() {
2978         return ( (this.bottom - this.top) * (this.right - this.left) );
2979     },
2980
2981     intersect : function(region) {
2982         var t = Math.max(this.top, region.top);
2983         var r = Math.min(this.right, region.right);
2984         var b = Math.min(this.bottom, region.bottom);
2985         var l = Math.max(this.left, region.left);
2986
2987         if (b >= t && r >= l) {
2988             return new Roo.lib.Region(t, r, b, l);
2989         } else {
2990             return null;
2991         }
2992     },
2993     union : function(region) {
2994         var t = Math.min(this.top, region.top);
2995         var r = Math.max(this.right, region.right);
2996         var b = Math.max(this.bottom, region.bottom);
2997         var l = Math.min(this.left, region.left);
2998
2999         return new Roo.lib.Region(t, r, b, l);
3000     },
3001
3002     adjust : function(t, l, b, r) {
3003         this.top += t;
3004         this.left += l;
3005         this.right += r;
3006         this.bottom += b;
3007         return this;
3008     }
3009 };
3010
3011 Roo.lib.Region.getRegion = function(el) {
3012     var p = Roo.lib.Dom.getXY(el);
3013
3014     var t = p[1];
3015     var r = p[0] + el.offsetWidth;
3016     var b = p[1] + el.offsetHeight;
3017     var l = p[0];
3018
3019     return new Roo.lib.Region(t, r, b, l);
3020 };
3021 /*
3022  * Portions of this file are based on pieces of Yahoo User Interface Library
3023  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3024  * YUI licensed under the BSD License:
3025  * http://developer.yahoo.net/yui/license.txt
3026  * <script type="text/javascript">
3027  *
3028  */
3029 //@@dep Roo.lib.Region
3030
3031
3032 Roo.lib.Point = function(x, y) {
3033     if (x instanceof Array) {
3034         y = x[1];
3035         x = x[0];
3036     }
3037     this.x = this.right = this.left = this[0] = x;
3038     this.y = this.top = this.bottom = this[1] = y;
3039 };
3040
3041 Roo.lib.Point.prototype = new Roo.lib.Region();
3042 /*
3043  * Portions of this file are based on pieces of Yahoo User Interface Library
3044  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3045  * YUI licensed under the BSD License:
3046  * http://developer.yahoo.net/yui/license.txt
3047  * <script type="text/javascript">
3048  *
3049  */
3050  
3051 (function() {   
3052
3053     Roo.lib.Anim = {
3054         scroll : function(el, args, duration, easing, cb, scope) {
3055             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3056         },
3057
3058         motion : function(el, args, duration, easing, cb, scope) {
3059             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3060         },
3061
3062         color : function(el, args, duration, easing, cb, scope) {
3063             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3064         },
3065
3066         run : function(el, args, duration, easing, cb, scope, type) {
3067             type = type || Roo.lib.AnimBase;
3068             if (typeof easing == "string") {
3069                 easing = Roo.lib.Easing[easing];
3070             }
3071             var anim = new type(el, args, duration, easing);
3072             anim.animateX(function() {
3073                 Roo.callback(cb, scope);
3074             });
3075             return anim;
3076         }
3077     };
3078 })();/*
3079  * Portions of this file are based on pieces of Yahoo User Interface Library
3080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3081  * YUI licensed under the BSD License:
3082  * http://developer.yahoo.net/yui/license.txt
3083  * <script type="text/javascript">
3084  *
3085  */
3086
3087 (function() {    
3088     var libFlyweight;
3089     
3090     function fly(el) {
3091         if (!libFlyweight) {
3092             libFlyweight = new Roo.Element.Flyweight();
3093         }
3094         libFlyweight.dom = el;
3095         return libFlyweight;
3096     }
3097
3098     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3099     
3100    
3101     
3102     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3103         if (el) {
3104             this.init(el, attributes, duration, method);
3105         }
3106     };
3107
3108     Roo.lib.AnimBase.fly = fly;
3109     
3110     
3111     
3112     Roo.lib.AnimBase.prototype = {
3113
3114         toString: function() {
3115             var el = this.getEl();
3116             var id = el.id || el.tagName;
3117             return ("Anim " + id);
3118         },
3119
3120         patterns: {
3121             noNegatives:        /width|height|opacity|padding/i,
3122             offsetAttribute:  /^((width|height)|(top|left))$/,
3123             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3124             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3125         },
3126
3127
3128         doMethod: function(attr, start, end) {
3129             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3130         },
3131
3132
3133         setAttribute: function(attr, val, unit) {
3134             if (this.patterns.noNegatives.test(attr)) {
3135                 val = (val > 0) ? val : 0;
3136             }
3137
3138             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3139         },
3140
3141
3142         getAttribute: function(attr) {
3143             var el = this.getEl();
3144             var val = fly(el).getStyle(attr);
3145
3146             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3147                 return parseFloat(val);
3148             }
3149
3150             var a = this.patterns.offsetAttribute.exec(attr) || [];
3151             var pos = !!( a[3] );
3152             var box = !!( a[2] );
3153
3154
3155             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3156                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3157             } else {
3158                 val = 0;
3159             }
3160
3161             return val;
3162         },
3163
3164
3165         getDefaultUnit: function(attr) {
3166             if (this.patterns.defaultUnit.test(attr)) {
3167                 return 'px';
3168             }
3169
3170             return '';
3171         },
3172
3173         animateX : function(callback, scope) {
3174             var f = function() {
3175                 this.onComplete.removeListener(f);
3176                 if (typeof callback == "function") {
3177                     callback.call(scope || this, this);
3178                 }
3179             };
3180             this.onComplete.addListener(f, this);
3181             this.animate();
3182         },
3183
3184
3185         setRuntimeAttribute: function(attr) {
3186             var start;
3187             var end;
3188             var attributes = this.attributes;
3189
3190             this.runtimeAttributes[attr] = {};
3191
3192             var isset = function(prop) {
3193                 return (typeof prop !== 'undefined');
3194             };
3195
3196             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3197                 return false;
3198             }
3199
3200             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3201
3202
3203             if (isset(attributes[attr]['to'])) {
3204                 end = attributes[attr]['to'];
3205             } else if (isset(attributes[attr]['by'])) {
3206                 if (start.constructor == Array) {
3207                     end = [];
3208                     for (var i = 0, len = start.length; i < len; ++i) {
3209                         end[i] = start[i] + attributes[attr]['by'][i];
3210                     }
3211                 } else {
3212                     end = start + attributes[attr]['by'];
3213                 }
3214             }
3215
3216             this.runtimeAttributes[attr].start = start;
3217             this.runtimeAttributes[attr].end = end;
3218
3219
3220             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3221         },
3222
3223
3224         init: function(el, attributes, duration, method) {
3225
3226             var isAnimated = false;
3227
3228
3229             var startTime = null;
3230
3231
3232             var actualFrames = 0;
3233
3234
3235             el = Roo.getDom(el);
3236
3237
3238             this.attributes = attributes || {};
3239
3240
3241             this.duration = duration || 1;
3242
3243
3244             this.method = method || Roo.lib.Easing.easeNone;
3245
3246
3247             this.useSeconds = true;
3248
3249
3250             this.currentFrame = 0;
3251
3252
3253             this.totalFrames = Roo.lib.AnimMgr.fps;
3254
3255
3256             this.getEl = function() {
3257                 return el;
3258             };
3259
3260
3261             this.isAnimated = function() {
3262                 return isAnimated;
3263             };
3264
3265
3266             this.getStartTime = function() {
3267                 return startTime;
3268             };
3269
3270             this.runtimeAttributes = {};
3271
3272
3273             this.animate = function() {
3274                 if (this.isAnimated()) {
3275                     return false;
3276                 }
3277
3278                 this.currentFrame = 0;
3279
3280                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3281
3282                 Roo.lib.AnimMgr.registerElement(this);
3283             };
3284
3285
3286             this.stop = function(finish) {
3287                 if (finish) {
3288                     this.currentFrame = this.totalFrames;
3289                     this._onTween.fire();
3290                 }
3291                 Roo.lib.AnimMgr.stop(this);
3292             };
3293
3294             var onStart = function() {
3295                 this.onStart.fire();
3296
3297                 this.runtimeAttributes = {};
3298                 for (var attr in this.attributes) {
3299                     this.setRuntimeAttribute(attr);
3300                 }
3301
3302                 isAnimated = true;
3303                 actualFrames = 0;
3304                 startTime = new Date();
3305             };
3306
3307
3308             var onTween = function() {
3309                 var data = {
3310                     duration: new Date() - this.getStartTime(),
3311                     currentFrame: this.currentFrame
3312                 };
3313
3314                 data.toString = function() {
3315                     return (
3316                             'duration: ' + data.duration +
3317                             ', currentFrame: ' + data.currentFrame
3318                             );
3319                 };
3320
3321                 this.onTween.fire(data);
3322
3323                 var runtimeAttributes = this.runtimeAttributes;
3324
3325                 for (var attr in runtimeAttributes) {
3326                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3327                 }
3328
3329                 actualFrames += 1;
3330             };
3331
3332             var onComplete = function() {
3333                 var actual_duration = (new Date() - startTime) / 1000 ;
3334
3335                 var data = {
3336                     duration: actual_duration,
3337                     frames: actualFrames,
3338                     fps: actualFrames / actual_duration
3339                 };
3340
3341                 data.toString = function() {
3342                     return (
3343                             'duration: ' + data.duration +
3344                             ', frames: ' + data.frames +
3345                             ', fps: ' + data.fps
3346                             );
3347                 };
3348
3349                 isAnimated = false;
3350                 actualFrames = 0;
3351                 this.onComplete.fire(data);
3352             };
3353
3354
3355             this._onStart = new Roo.util.Event(this);
3356             this.onStart = new Roo.util.Event(this);
3357             this.onTween = new Roo.util.Event(this);
3358             this._onTween = new Roo.util.Event(this);
3359             this.onComplete = new Roo.util.Event(this);
3360             this._onComplete = new Roo.util.Event(this);
3361             this._onStart.addListener(onStart);
3362             this._onTween.addListener(onTween);
3363             this._onComplete.addListener(onComplete);
3364         }
3365     };
3366 })();
3367 /*
3368  * Portions of this file are based on pieces of Yahoo User Interface Library
3369  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3370  * YUI licensed under the BSD License:
3371  * http://developer.yahoo.net/yui/license.txt
3372  * <script type="text/javascript">
3373  *
3374  */
3375
3376 Roo.lib.AnimMgr = new function() {
3377
3378         var thread = null;
3379
3380
3381         var queue = [];
3382
3383
3384         var tweenCount = 0;
3385
3386
3387         this.fps = 1000;
3388
3389
3390         this.delay = 1;
3391
3392
3393         this.registerElement = function(tween) {
3394             queue[queue.length] = tween;
3395             tweenCount += 1;
3396             tween._onStart.fire();
3397             this.start();
3398         };
3399
3400
3401         this.unRegister = function(tween, index) {
3402             tween._onComplete.fire();
3403             index = index || getIndex(tween);
3404             if (index != -1) {
3405                 queue.splice(index, 1);
3406             }
3407
3408             tweenCount -= 1;
3409             if (tweenCount <= 0) {
3410                 this.stop();
3411             }
3412         };
3413
3414
3415         this.start = function() {
3416             if (thread === null) {
3417                 thread = setInterval(this.run, this.delay);
3418             }
3419         };
3420
3421
3422         this.stop = function(tween) {
3423             if (!tween) {
3424                 clearInterval(thread);
3425
3426                 for (var i = 0, len = queue.length; i < len; ++i) {
3427                     if (queue[0].isAnimated()) {
3428                         this.unRegister(queue[0], 0);
3429                     }
3430                 }
3431
3432                 queue = [];
3433                 thread = null;
3434                 tweenCount = 0;
3435             }
3436             else {
3437                 this.unRegister(tween);
3438             }
3439         };
3440
3441
3442         this.run = function() {
3443             for (var i = 0, len = queue.length; i < len; ++i) {
3444                 var tween = queue[i];
3445                 if (!tween || !tween.isAnimated()) {
3446                     continue;
3447                 }
3448
3449                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3450                 {
3451                     tween.currentFrame += 1;
3452
3453                     if (tween.useSeconds) {
3454                         correctFrame(tween);
3455                     }
3456                     tween._onTween.fire();
3457                 }
3458                 else {
3459                     Roo.lib.AnimMgr.stop(tween, i);
3460                 }
3461             }
3462         };
3463
3464         var getIndex = function(anim) {
3465             for (var i = 0, len = queue.length; i < len; ++i) {
3466                 if (queue[i] == anim) {
3467                     return i;
3468                 }
3469             }
3470             return -1;
3471         };
3472
3473
3474         var correctFrame = function(tween) {
3475             var frames = tween.totalFrames;
3476             var frame = tween.currentFrame;
3477             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3478             var elapsed = (new Date() - tween.getStartTime());
3479             var tweak = 0;
3480
3481             if (elapsed < tween.duration * 1000) {
3482                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3483             } else {
3484                 tweak = frames - (frame + 1);
3485             }
3486             if (tweak > 0 && isFinite(tweak)) {
3487                 if (tween.currentFrame + tweak >= frames) {
3488                     tweak = frames - (frame + 1);
3489                 }
3490
3491                 tween.currentFrame += tweak;
3492             }
3493         };
3494     };/*
3495  * Portions of this file are based on pieces of Yahoo User Interface Library
3496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3497  * YUI licensed under the BSD License:
3498  * http://developer.yahoo.net/yui/license.txt
3499  * <script type="text/javascript">
3500  *
3501  */
3502 Roo.lib.Bezier = new function() {
3503
3504         this.getPosition = function(points, t) {
3505             var n = points.length;
3506             var tmp = [];
3507
3508             for (var i = 0; i < n; ++i) {
3509                 tmp[i] = [points[i][0], points[i][1]];
3510             }
3511
3512             for (var j = 1; j < n; ++j) {
3513                 for (i = 0; i < n - j; ++i) {
3514                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3515                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3516                 }
3517             }
3518
3519             return [ tmp[0][0], tmp[0][1] ];
3520
3521         };
3522     };/*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 (function() {
3531
3532     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3533         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3534     };
3535
3536     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3537
3538     var fly = Roo.lib.AnimBase.fly;
3539     var Y = Roo.lib;
3540     var superclass = Y.ColorAnim.superclass;
3541     var proto = Y.ColorAnim.prototype;
3542
3543     proto.toString = function() {
3544         var el = this.getEl();
3545         var id = el.id || el.tagName;
3546         return ("ColorAnim " + id);
3547     };
3548
3549     proto.patterns.color = /color$/i;
3550     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3551     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3552     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3553     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3554
3555
3556     proto.parseColor = function(s) {
3557         if (s.length == 3) {
3558             return s;
3559         }
3560
3561         var c = this.patterns.hex.exec(s);
3562         if (c && c.length == 4) {
3563             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3564         }
3565
3566         c = this.patterns.rgb.exec(s);
3567         if (c && c.length == 4) {
3568             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3569         }
3570
3571         c = this.patterns.hex3.exec(s);
3572         if (c && c.length == 4) {
3573             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3574         }
3575
3576         return null;
3577     };
3578     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3579     proto.getAttribute = function(attr) {
3580         var el = this.getEl();
3581         if (this.patterns.color.test(attr)) {
3582             var val = fly(el).getStyle(attr);
3583
3584             if (this.patterns.transparent.test(val)) {
3585                 var parent = el.parentNode;
3586                 val = fly(parent).getStyle(attr);
3587
3588                 while (parent && this.patterns.transparent.test(val)) {
3589                     parent = parent.parentNode;
3590                     val = fly(parent).getStyle(attr);
3591                     if (parent.tagName.toUpperCase() == 'HTML') {
3592                         val = '#fff';
3593                     }
3594                 }
3595             }
3596         } else {
3597             val = superclass.getAttribute.call(this, attr);
3598         }
3599
3600         return val;
3601     };
3602     proto.getAttribute = function(attr) {
3603         var el = this.getEl();
3604         if (this.patterns.color.test(attr)) {
3605             var val = fly(el).getStyle(attr);
3606
3607             if (this.patterns.transparent.test(val)) {
3608                 var parent = el.parentNode;
3609                 val = fly(parent).getStyle(attr);
3610
3611                 while (parent && this.patterns.transparent.test(val)) {
3612                     parent = parent.parentNode;
3613                     val = fly(parent).getStyle(attr);
3614                     if (parent.tagName.toUpperCase() == 'HTML') {
3615                         val = '#fff';
3616                     }
3617                 }
3618             }
3619         } else {
3620             val = superclass.getAttribute.call(this, attr);
3621         }
3622
3623         return val;
3624     };
3625
3626     proto.doMethod = function(attr, start, end) {
3627         var val;
3628
3629         if (this.patterns.color.test(attr)) {
3630             val = [];
3631             for (var i = 0, len = start.length; i < len; ++i) {
3632                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3633             }
3634
3635             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3636         }
3637         else {
3638             val = superclass.doMethod.call(this, attr, start, end);
3639         }
3640
3641         return val;
3642     };
3643
3644     proto.setRuntimeAttribute = function(attr) {
3645         superclass.setRuntimeAttribute.call(this, attr);
3646
3647         if (this.patterns.color.test(attr)) {
3648             var attributes = this.attributes;
3649             var start = this.parseColor(this.runtimeAttributes[attr].start);
3650             var end = this.parseColor(this.runtimeAttributes[attr].end);
3651
3652             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3653                 end = this.parseColor(attributes[attr].by);
3654
3655                 for (var i = 0, len = start.length; i < len; ++i) {
3656                     end[i] = start[i] + end[i];
3657                 }
3658             }
3659
3660             this.runtimeAttributes[attr].start = start;
3661             this.runtimeAttributes[attr].end = end;
3662         }
3663     };
3664 })();
3665
3666 /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Easing = {
3675
3676
3677     easeNone: function (t, b, c, d) {
3678         return c * t / d + b;
3679     },
3680
3681
3682     easeIn: function (t, b, c, d) {
3683         return c * (t /= d) * t + b;
3684     },
3685
3686
3687     easeOut: function (t, b, c, d) {
3688         return -c * (t /= d) * (t - 2) + b;
3689     },
3690
3691
3692     easeBoth: function (t, b, c, d) {
3693         if ((t /= d / 2) < 1) {
3694             return c / 2 * t * t + b;
3695         }
3696
3697         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3698     },
3699
3700
3701     easeInStrong: function (t, b, c, d) {
3702         return c * (t /= d) * t * t * t + b;
3703     },
3704
3705
3706     easeOutStrong: function (t, b, c, d) {
3707         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3708     },
3709
3710
3711     easeBothStrong: function (t, b, c, d) {
3712         if ((t /= d / 2) < 1) {
3713             return c / 2 * t * t * t * t + b;
3714         }
3715
3716         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3717     },
3718
3719
3720
3721     elasticIn: function (t, b, c, d, a, p) {
3722         if (t == 0) {
3723             return b;
3724         }
3725         if ((t /= d) == 1) {
3726             return b + c;
3727         }
3728         if (!p) {
3729             p = d * .3;
3730         }
3731
3732         if (!a || a < Math.abs(c)) {
3733             a = c;
3734             var s = p / 4;
3735         }
3736         else {
3737             var s = p / (2 * Math.PI) * Math.asin(c / a);
3738         }
3739
3740         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3741     },
3742
3743
3744     elasticOut: function (t, b, c, d, a, p) {
3745         if (t == 0) {
3746             return b;
3747         }
3748         if ((t /= d) == 1) {
3749             return b + c;
3750         }
3751         if (!p) {
3752             p = d * .3;
3753         }
3754
3755         if (!a || a < Math.abs(c)) {
3756             a = c;
3757             var s = p / 4;
3758         }
3759         else {
3760             var s = p / (2 * Math.PI) * Math.asin(c / a);
3761         }
3762
3763         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3764     },
3765
3766
3767     elasticBoth: function (t, b, c, d, a, p) {
3768         if (t == 0) {
3769             return b;
3770         }
3771
3772         if ((t /= d / 2) == 2) {
3773             return b + c;
3774         }
3775
3776         if (!p) {
3777             p = d * (.3 * 1.5);
3778         }
3779
3780         if (!a || a < Math.abs(c)) {
3781             a = c;
3782             var s = p / 4;
3783         }
3784         else {
3785             var s = p / (2 * Math.PI) * Math.asin(c / a);
3786         }
3787
3788         if (t < 1) {
3789             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3790                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3791         }
3792         return a * Math.pow(2, -10 * (t -= 1)) *
3793                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3794     },
3795
3796
3797
3798     backIn: function (t, b, c, d, s) {
3799         if (typeof s == 'undefined') {
3800             s = 1.70158;
3801         }
3802         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3803     },
3804
3805
3806     backOut: function (t, b, c, d, s) {
3807         if (typeof s == 'undefined') {
3808             s = 1.70158;
3809         }
3810         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3811     },
3812
3813
3814     backBoth: function (t, b, c, d, s) {
3815         if (typeof s == 'undefined') {
3816             s = 1.70158;
3817         }
3818
3819         if ((t /= d / 2 ) < 1) {
3820             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3821         }
3822         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3823     },
3824
3825
3826     bounceIn: function (t, b, c, d) {
3827         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3828     },
3829
3830
3831     bounceOut: function (t, b, c, d) {
3832         if ((t /= d) < (1 / 2.75)) {
3833             return c * (7.5625 * t * t) + b;
3834         } else if (t < (2 / 2.75)) {
3835             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3836         } else if (t < (2.5 / 2.75)) {
3837             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3838         }
3839         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3840     },
3841
3842
3843     bounceBoth: function (t, b, c, d) {
3844         if (t < d / 2) {
3845             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3846         }
3847         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3848     }
3849 };/*
3850  * Portions of this file are based on pieces of Yahoo User Interface Library
3851  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3852  * YUI licensed under the BSD License:
3853  * http://developer.yahoo.net/yui/license.txt
3854  * <script type="text/javascript">
3855  *
3856  */
3857     (function() {
3858         Roo.lib.Motion = function(el, attributes, duration, method) {
3859             if (el) {
3860                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3861             }
3862         };
3863
3864         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3865
3866
3867         var Y = Roo.lib;
3868         var superclass = Y.Motion.superclass;
3869         var proto = Y.Motion.prototype;
3870
3871         proto.toString = function() {
3872             var el = this.getEl();
3873             var id = el.id || el.tagName;
3874             return ("Motion " + id);
3875         };
3876
3877         proto.patterns.points = /^points$/i;
3878
3879         proto.setAttribute = function(attr, val, unit) {
3880             if (this.patterns.points.test(attr)) {
3881                 unit = unit || 'px';
3882                 superclass.setAttribute.call(this, 'left', val[0], unit);
3883                 superclass.setAttribute.call(this, 'top', val[1], unit);
3884             } else {
3885                 superclass.setAttribute.call(this, attr, val, unit);
3886             }
3887         };
3888
3889         proto.getAttribute = function(attr) {
3890             if (this.patterns.points.test(attr)) {
3891                 var val = [
3892                         superclass.getAttribute.call(this, 'left'),
3893                         superclass.getAttribute.call(this, 'top')
3894                         ];
3895             } else {
3896                 val = superclass.getAttribute.call(this, attr);
3897             }
3898
3899             return val;
3900         };
3901
3902         proto.doMethod = function(attr, start, end) {
3903             var val = null;
3904
3905             if (this.patterns.points.test(attr)) {
3906                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3907                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3908             } else {
3909                 val = superclass.doMethod.call(this, attr, start, end);
3910             }
3911             return val;
3912         };
3913
3914         proto.setRuntimeAttribute = function(attr) {
3915             if (this.patterns.points.test(attr)) {
3916                 var el = this.getEl();
3917                 var attributes = this.attributes;
3918                 var start;
3919                 var control = attributes['points']['control'] || [];
3920                 var end;
3921                 var i, len;
3922
3923                 if (control.length > 0 && !(control[0] instanceof Array)) {
3924                     control = [control];
3925                 } else {
3926                     var tmp = [];
3927                     for (i = 0,len = control.length; i < len; ++i) {
3928                         tmp[i] = control[i];
3929                     }
3930                     control = tmp;
3931                 }
3932
3933                 Roo.fly(el).position();
3934
3935                 if (isset(attributes['points']['from'])) {
3936                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3937                 }
3938                 else {
3939                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3940                 }
3941
3942                 start = this.getAttribute('points');
3943
3944
3945                 if (isset(attributes['points']['to'])) {
3946                     end = translateValues.call(this, attributes['points']['to'], start);
3947
3948                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3949                     for (i = 0,len = control.length; i < len; ++i) {
3950                         control[i] = translateValues.call(this, control[i], start);
3951                     }
3952
3953
3954                 } else if (isset(attributes['points']['by'])) {
3955                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3956
3957                     for (i = 0,len = control.length; i < len; ++i) {
3958                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3959                     }
3960                 }
3961
3962                 this.runtimeAttributes[attr] = [start];
3963
3964                 if (control.length > 0) {
3965                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3966                 }
3967
3968                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3969             }
3970             else {
3971                 superclass.setRuntimeAttribute.call(this, attr);
3972             }
3973         };
3974
3975         var translateValues = function(val, start) {
3976             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3978
3979             return val;
3980         };
3981
3982         var isset = function(prop) {
3983             return (typeof prop !== 'undefined');
3984         };
3985     })();
3986 /*
3987  * Portions of this file are based on pieces of Yahoo User Interface Library
3988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3989  * YUI licensed under the BSD License:
3990  * http://developer.yahoo.net/yui/license.txt
3991  * <script type="text/javascript">
3992  *
3993  */
3994     (function() {
3995         Roo.lib.Scroll = function(el, attributes, duration, method) {
3996             if (el) {
3997                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3998             }
3999         };
4000
4001         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4002
4003
4004         var Y = Roo.lib;
4005         var superclass = Y.Scroll.superclass;
4006         var proto = Y.Scroll.prototype;
4007
4008         proto.toString = function() {
4009             var el = this.getEl();
4010             var id = el.id || el.tagName;
4011             return ("Scroll " + id);
4012         };
4013
4014         proto.doMethod = function(attr, start, end) {
4015             var val = null;
4016
4017             if (attr == 'scroll') {
4018                 val = [
4019                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4020                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4021                         ];
4022
4023             } else {
4024                 val = superclass.doMethod.call(this, attr, start, end);
4025             }
4026             return val;
4027         };
4028
4029         proto.getAttribute = function(attr) {
4030             var val = null;
4031             var el = this.getEl();
4032
4033             if (attr == 'scroll') {
4034                 val = [ el.scrollLeft, el.scrollTop ];
4035             } else {
4036                 val = superclass.getAttribute.call(this, attr);
4037             }
4038
4039             return val;
4040         };
4041
4042         proto.setAttribute = function(attr, val, unit) {
4043             var el = this.getEl();
4044
4045             if (attr == 'scroll') {
4046                 el.scrollLeft = val[0];
4047                 el.scrollTop = val[1];
4048             } else {
4049                 superclass.setAttribute.call(this, attr, val, unit);
4050             }
4051         };
4052     })();
4053 /*
4054  * Based on:
4055  * Ext JS Library 1.1.1
4056  * Copyright(c) 2006-2007, Ext JS, LLC.
4057  *
4058  * Originally Released Under LGPL - original licence link has changed is not relivant.
4059  *
4060  * Fork - LGPL
4061  * <script type="text/javascript">
4062  */
4063
4064
4065 // nasty IE9 hack - what a pile of crap that is..
4066
4067  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4068     Range.prototype.createContextualFragment = function (html) {
4069         var doc = window.document;
4070         var container = doc.createElement("div");
4071         container.innerHTML = html;
4072         var frag = doc.createDocumentFragment(), n;
4073         while ((n = container.firstChild)) {
4074             frag.appendChild(n);
4075         }
4076         return frag;
4077     };
4078 }
4079
4080 /**
4081  * @class Roo.DomHelper
4082  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4083  * 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>.
4084  * @singleton
4085  */
4086 Roo.DomHelper = function(){
4087     var tempTableEl = null;
4088     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4089     var tableRe = /^table|tbody|tr|td$/i;
4090     var xmlns = {};
4091     // build as innerHTML where available
4092     /** @ignore */
4093     var createHtml = function(o){
4094         if(typeof o == 'string'){
4095             return o;
4096         }
4097         var b = "";
4098         if(!o.tag){
4099             o.tag = "div";
4100         }
4101         b += "<" + o.tag;
4102         for(var attr in o){
4103             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4104             if(attr == "style"){
4105                 var s = o["style"];
4106                 if(typeof s == "function"){
4107                     s = s.call();
4108                 }
4109                 if(typeof s == "string"){
4110                     b += ' style="' + s + '"';
4111                 }else if(typeof s == "object"){
4112                     b += ' style="';
4113                     for(var key in s){
4114                         if(typeof s[key] != "function"){
4115                             b += key + ":" + s[key] + ";";
4116                         }
4117                     }
4118                     b += '"';
4119                 }
4120             }else{
4121                 if(attr == "cls"){
4122                     b += ' class="' + o["cls"] + '"';
4123                 }else if(attr == "htmlFor"){
4124                     b += ' for="' + o["htmlFor"] + '"';
4125                 }else{
4126                     b += " " + attr + '="' + o[attr] + '"';
4127                 }
4128             }
4129         }
4130         if(emptyTags.test(o.tag)){
4131             b += "/>";
4132         }else{
4133             b += ">";
4134             var cn = o.children || o.cn;
4135             if(cn){
4136                 //http://bugs.kde.org/show_bug.cgi?id=71506
4137                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4138                     for(var i = 0, len = cn.length; i < len; i++) {
4139                         b += createHtml(cn[i], b);
4140                     }
4141                 }else{
4142                     b += createHtml(cn, b);
4143                 }
4144             }
4145             if(o.html){
4146                 b += o.html;
4147             }
4148             b += "</" + o.tag + ">";
4149         }
4150         return b;
4151     };
4152
4153     // build as dom
4154     /** @ignore */
4155     var createDom = function(o, parentNode){
4156          
4157         // defininition craeted..
4158         var ns = false;
4159         if (o.ns && o.ns != 'html') {
4160                
4161             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4162                 xmlns[o.ns] = o.xmlns;
4163                 ns = o.xmlns;
4164             }
4165             if (typeof(xmlns[o.ns]) == 'undefined') {
4166                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4167             }
4168             ns = xmlns[o.ns];
4169         }
4170         
4171         
4172         if (typeof(o) == 'string') {
4173             return parentNode.appendChild(document.createTextNode(o));
4174         }
4175         o.tag = o.tag || div;
4176         if (o.ns && Roo.isIE) {
4177             ns = false;
4178             o.tag = o.ns + ':' + o.tag;
4179             
4180         }
4181         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4182         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4183         for(var attr in o){
4184             
4185             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4186                     attr == "style" || typeof o[attr] == "function") continue;
4187                     
4188             if(attr=="cls" && Roo.isIE){
4189                 el.className = o["cls"];
4190             }else{
4191                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4192                 else el[attr] = o[attr];
4193             }
4194         }
4195         Roo.DomHelper.applyStyles(el, o.style);
4196         var cn = o.children || o.cn;
4197         if(cn){
4198             //http://bugs.kde.org/show_bug.cgi?id=71506
4199              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4200                 for(var i = 0, len = cn.length; i < len; i++) {
4201                     createDom(cn[i], el);
4202                 }
4203             }else{
4204                 createDom(cn, el);
4205             }
4206         }
4207         if(o.html){
4208             el.innerHTML = o.html;
4209         }
4210         if(parentNode){
4211            parentNode.appendChild(el);
4212         }
4213         return el;
4214     };
4215
4216     var ieTable = function(depth, s, h, e){
4217         tempTableEl.innerHTML = [s, h, e].join('');
4218         var i = -1, el = tempTableEl;
4219         while(++i < depth){
4220             el = el.firstChild;
4221         }
4222         return el;
4223     };
4224
4225     // kill repeat to save bytes
4226     var ts = '<table>',
4227         te = '</table>',
4228         tbs = ts+'<tbody>',
4229         tbe = '</tbody>'+te,
4230         trs = tbs + '<tr>',
4231         tre = '</tr>'+tbe;
4232
4233     /**
4234      * @ignore
4235      * Nasty code for IE's broken table implementation
4236      */
4237     var insertIntoTable = function(tag, where, el, html){
4238         if(!tempTableEl){
4239             tempTableEl = document.createElement('div');
4240         }
4241         var node;
4242         var before = null;
4243         if(tag == 'td'){
4244             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4245                 return;
4246             }
4247             if(where == 'beforebegin'){
4248                 before = el;
4249                 el = el.parentNode;
4250             } else{
4251                 before = el.nextSibling;
4252                 el = el.parentNode;
4253             }
4254             node = ieTable(4, trs, html, tre);
4255         }
4256         else if(tag == 'tr'){
4257             if(where == 'beforebegin'){
4258                 before = el;
4259                 el = el.parentNode;
4260                 node = ieTable(3, tbs, html, tbe);
4261             } else if(where == 'afterend'){
4262                 before = el.nextSibling;
4263                 el = el.parentNode;
4264                 node = ieTable(3, tbs, html, tbe);
4265             } else{ // INTO a TR
4266                 if(where == 'afterbegin'){
4267                     before = el.firstChild;
4268                 }
4269                 node = ieTable(4, trs, html, tre);
4270             }
4271         } else if(tag == 'tbody'){
4272             if(where == 'beforebegin'){
4273                 before = el;
4274                 el = el.parentNode;
4275                 node = ieTable(2, ts, html, te);
4276             } else if(where == 'afterend'){
4277                 before = el.nextSibling;
4278                 el = el.parentNode;
4279                 node = ieTable(2, ts, html, te);
4280             } else{
4281                 if(where == 'afterbegin'){
4282                     before = el.firstChild;
4283                 }
4284                 node = ieTable(3, tbs, html, tbe);
4285             }
4286         } else{ // TABLE
4287             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4288                 return;
4289             }
4290             if(where == 'afterbegin'){
4291                 before = el.firstChild;
4292             }
4293             node = ieTable(2, ts, html, te);
4294         }
4295         el.insertBefore(node, before);
4296         return node;
4297     };
4298
4299     return {
4300     /** True to force the use of DOM instead of html fragments @type Boolean */
4301     useDom : false,
4302
4303     /**
4304      * Returns the markup for the passed Element(s) config
4305      * @param {Object} o The Dom object spec (and children)
4306      * @return {String}
4307      */
4308     markup : function(o){
4309         return createHtml(o);
4310     },
4311
4312     /**
4313      * Applies a style specification to an element
4314      * @param {String/HTMLElement} el The element to apply styles to
4315      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4316      * a function which returns such a specification.
4317      */
4318     applyStyles : function(el, styles){
4319         if(styles){
4320            el = Roo.fly(el);
4321            if(typeof styles == "string"){
4322                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4323                var matches;
4324                while ((matches = re.exec(styles)) != null){
4325                    el.setStyle(matches[1], matches[2]);
4326                }
4327            }else if (typeof styles == "object"){
4328                for (var style in styles){
4329                   el.setStyle(style, styles[style]);
4330                }
4331            }else if (typeof styles == "function"){
4332                 Roo.DomHelper.applyStyles(el, styles.call());
4333            }
4334         }
4335     },
4336
4337     /**
4338      * Inserts an HTML fragment into the Dom
4339      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4340      * @param {HTMLElement} el The context element
4341      * @param {String} html The HTML fragmenet
4342      * @return {HTMLElement} The new node
4343      */
4344     insertHtml : function(where, el, html){
4345         where = where.toLowerCase();
4346         if(el.insertAdjacentHTML){
4347             if(tableRe.test(el.tagName)){
4348                 var rs;
4349                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4350                     return rs;
4351                 }
4352             }
4353             switch(where){
4354                 case "beforebegin":
4355                     el.insertAdjacentHTML('BeforeBegin', html);
4356                     return el.previousSibling;
4357                 case "afterbegin":
4358                     el.insertAdjacentHTML('AfterBegin', html);
4359                     return el.firstChild;
4360                 case "beforeend":
4361                     el.insertAdjacentHTML('BeforeEnd', html);
4362                     return el.lastChild;
4363                 case "afterend":
4364                     el.insertAdjacentHTML('AfterEnd', html);
4365                     return el.nextSibling;
4366             }
4367             throw 'Illegal insertion point -> "' + where + '"';
4368         }
4369         var range = el.ownerDocument.createRange();
4370         var frag;
4371         switch(where){
4372              case "beforebegin":
4373                 range.setStartBefore(el);
4374                 frag = range.createContextualFragment(html);
4375                 el.parentNode.insertBefore(frag, el);
4376                 return el.previousSibling;
4377              case "afterbegin":
4378                 if(el.firstChild){
4379                     range.setStartBefore(el.firstChild);
4380                     frag = range.createContextualFragment(html);
4381                     el.insertBefore(frag, el.firstChild);
4382                     return el.firstChild;
4383                 }else{
4384                     el.innerHTML = html;
4385                     return el.firstChild;
4386                 }
4387             case "beforeend":
4388                 if(el.lastChild){
4389                     range.setStartAfter(el.lastChild);
4390                     frag = range.createContextualFragment(html);
4391                     el.appendChild(frag);
4392                     return el.lastChild;
4393                 }else{
4394                     el.innerHTML = html;
4395                     return el.lastChild;
4396                 }
4397             case "afterend":
4398                 range.setStartAfter(el);
4399                 frag = range.createContextualFragment(html);
4400                 el.parentNode.insertBefore(frag, el.nextSibling);
4401                 return el.nextSibling;
4402             }
4403             throw 'Illegal insertion point -> "' + where + '"';
4404     },
4405
4406     /**
4407      * Creates new Dom element(s) and inserts them before el
4408      * @param {String/HTMLElement/Element} el The context element
4409      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4410      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4411      * @return {HTMLElement/Roo.Element} The new node
4412      */
4413     insertBefore : function(el, o, returnElement){
4414         return this.doInsert(el, o, returnElement, "beforeBegin");
4415     },
4416
4417     /**
4418      * Creates new Dom element(s) and inserts them after el
4419      * @param {String/HTMLElement/Element} el The context element
4420      * @param {Object} o The Dom object spec (and children)
4421      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4422      * @return {HTMLElement/Roo.Element} The new node
4423      */
4424     insertAfter : function(el, o, returnElement){
4425         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4426     },
4427
4428     /**
4429      * Creates new Dom element(s) and inserts them as the first child of el
4430      * @param {String/HTMLElement/Element} el The context element
4431      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4432      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4433      * @return {HTMLElement/Roo.Element} The new node
4434      */
4435     insertFirst : function(el, o, returnElement){
4436         return this.doInsert(el, o, returnElement, "afterBegin");
4437     },
4438
4439     // private
4440     doInsert : function(el, o, returnElement, pos, sibling){
4441         el = Roo.getDom(el);
4442         var newNode;
4443         if(this.useDom || o.ns){
4444             newNode = createDom(o, null);
4445             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4446         }else{
4447             var html = createHtml(o);
4448             newNode = this.insertHtml(pos, el, html);
4449         }
4450         return returnElement ? Roo.get(newNode, true) : newNode;
4451     },
4452
4453     /**
4454      * Creates new Dom element(s) and appends them to el
4455      * @param {String/HTMLElement/Element} el The context element
4456      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4457      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4458      * @return {HTMLElement/Roo.Element} The new node
4459      */
4460     append : function(el, o, returnElement){
4461         el = Roo.getDom(el);
4462         var newNode;
4463         if(this.useDom || o.ns){
4464             newNode = createDom(o, null);
4465             el.appendChild(newNode);
4466         }else{
4467             var html = createHtml(o);
4468             newNode = this.insertHtml("beforeEnd", el, html);
4469         }
4470         return returnElement ? Roo.get(newNode, true) : newNode;
4471     },
4472
4473     /**
4474      * Creates new Dom element(s) and overwrites the contents of el with them
4475      * @param {String/HTMLElement/Element} el The context element
4476      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4477      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4478      * @return {HTMLElement/Roo.Element} The new node
4479      */
4480     overwrite : function(el, o, returnElement){
4481         el = Roo.getDom(el);
4482         if (o.ns) {
4483           
4484             while (el.childNodes.length) {
4485                 el.removeChild(el.firstChild);
4486             }
4487             createDom(o, el);
4488         } else {
4489             el.innerHTML = createHtml(o);   
4490         }
4491         
4492         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4493     },
4494
4495     /**
4496      * Creates a new Roo.DomHelper.Template from the Dom object spec
4497      * @param {Object} o The Dom object spec (and children)
4498      * @return {Roo.DomHelper.Template} The new template
4499      */
4500     createTemplate : function(o){
4501         var html = createHtml(o);
4502         return new Roo.Template(html);
4503     }
4504     };
4505 }();
4506 /*
4507  * Based on:
4508  * Ext JS Library 1.1.1
4509  * Copyright(c) 2006-2007, Ext JS, LLC.
4510  *
4511  * Originally Released Under LGPL - original licence link has changed is not relivant.
4512  *
4513  * Fork - LGPL
4514  * <script type="text/javascript">
4515  */
4516  
4517 /**
4518 * @class Roo.Template
4519 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4520 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4521 * Usage:
4522 <pre><code>
4523 var t = new Roo.Template({
4524     html :  '&lt;div name="{id}"&gt;' + 
4525         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4526         '&lt;/div&gt;',
4527     myformat: function (value, allValues) {
4528         return 'XX' + value;
4529     }
4530 });
4531 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4532 </code></pre>
4533 * For more information see this blog post with examples:
4534 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4535      - Create Elements using DOM, HTML fragments and Templates</a>. 
4536 * @constructor
4537 * @param {Object} cfg - Configuration object.
4538 */
4539 Roo.Template = function(cfg){
4540     // BC!
4541     if(cfg instanceof Array){
4542         cfg = cfg.join("");
4543     }else if(arguments.length > 1){
4544         cfg = Array.prototype.join.call(arguments, "");
4545     }
4546     
4547     
4548     if (typeof(cfg) == 'object') {
4549         Roo.apply(this,cfg)
4550     } else {
4551         // bc
4552         this.html = cfg;
4553     }
4554     if (this.url) {
4555         this.load();
4556     }
4557     
4558 };
4559 Roo.Template.prototype = {
4560     
4561     /**
4562      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4563      *                    it should be fixed so that template is observable...
4564      */
4565     url : false,
4566     /**
4567      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4568      */
4569     html : '',
4570     /**
4571      * Returns an HTML fragment of this template with the specified values applied.
4572      * @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'})
4573      * @return {String} The HTML fragment
4574      */
4575     applyTemplate : function(values){
4576         try {
4577            
4578             if(this.compiled){
4579                 return this.compiled(values);
4580             }
4581             var useF = this.disableFormats !== true;
4582             var fm = Roo.util.Format, tpl = this;
4583             var fn = function(m, name, format, args){
4584                 if(format && useF){
4585                     if(format.substr(0, 5) == "this."){
4586                         return tpl.call(format.substr(5), values[name], values);
4587                     }else{
4588                         if(args){
4589                             // quoted values are required for strings in compiled templates, 
4590                             // but for non compiled we need to strip them
4591                             // quoted reversed for jsmin
4592                             var re = /^\s*['"](.*)["']\s*$/;
4593                             args = args.split(',');
4594                             for(var i = 0, len = args.length; i < len; i++){
4595                                 args[i] = args[i].replace(re, "$1");
4596                             }
4597                             args = [values[name]].concat(args);
4598                         }else{
4599                             args = [values[name]];
4600                         }
4601                         return fm[format].apply(fm, args);
4602                     }
4603                 }else{
4604                     return values[name] !== undefined ? values[name] : "";
4605                 }
4606             };
4607             return this.html.replace(this.re, fn);
4608         } catch (e) {
4609             Roo.log(e);
4610             throw e;
4611         }
4612          
4613     },
4614     
4615     loading : false,
4616       
4617     load : function ()
4618     {
4619          
4620         if (this.loading) {
4621             return;
4622         }
4623         var _t = this;
4624         
4625         this.loading = true;
4626         this.compiled = false;
4627         
4628         var cx = new Roo.data.Connection();
4629         cx.request({
4630             url : this.url,
4631             method : 'GET',
4632             success : function (response) {
4633                 _t.loading = false;
4634                 _t.html = response.responseText;
4635                 _t.url = false;
4636                 _t.compile();
4637              },
4638             failure : function(response) {
4639                 Roo.log("Template failed to load from " + url);
4640                 _t.loading = false;
4641             }
4642         });
4643     },
4644
4645     /**
4646      * Sets the HTML used as the template and optionally compiles it.
4647      * @param {String} html
4648      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4649      * @return {Roo.Template} this
4650      */
4651     set : function(html, compile){
4652         this.html = html;
4653         this.compiled = null;
4654         if(compile){
4655             this.compile();
4656         }
4657         return this;
4658     },
4659     
4660     /**
4661      * True to disable format functions (defaults to false)
4662      * @type Boolean
4663      */
4664     disableFormats : false,
4665     
4666     /**
4667     * The regular expression used to match template variables 
4668     * @type RegExp
4669     * @property 
4670     */
4671     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4672     
4673     /**
4674      * Compiles the template into an internal function, eliminating the RegEx overhead.
4675      * @return {Roo.Template} this
4676      */
4677     compile : function(){
4678         var fm = Roo.util.Format;
4679         var useF = this.disableFormats !== true;
4680         var sep = Roo.isGecko ? "+" : ",";
4681         var fn = function(m, name, format, args){
4682             if(format && useF){
4683                 args = args ? ',' + args : "";
4684                 if(format.substr(0, 5) != "this."){
4685                     format = "fm." + format + '(';
4686                 }else{
4687                     format = 'this.call("'+ format.substr(5) + '", ';
4688                     args = ", values";
4689                 }
4690             }else{
4691                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4692             }
4693             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4694         };
4695         var body;
4696         // branched to use + in gecko and [].join() in others
4697         if(Roo.isGecko){
4698             body = "this.compiled = function(values){ return '" +
4699                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4700                     "';};";
4701         }else{
4702             body = ["this.compiled = function(values){ return ['"];
4703             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4704             body.push("'].join('');};");
4705             body = body.join('');
4706         }
4707         /**
4708          * eval:var:values
4709          * eval:var:fm
4710          */
4711         eval(body);
4712         return this;
4713     },
4714     
4715     // private function used to call members
4716     call : function(fnName, value, allValues){
4717         return this[fnName](value, allValues);
4718     },
4719     
4720     /**
4721      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4722      * @param {String/HTMLElement/Roo.Element} el The context element
4723      * @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'})
4724      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4725      * @return {HTMLElement/Roo.Element} The new node or Element
4726      */
4727     insertFirst: function(el, values, returnElement){
4728         return this.doInsert('afterBegin', el, values, returnElement);
4729     },
4730
4731     /**
4732      * Applies the supplied values to the template and inserts the new node(s) before el.
4733      * @param {String/HTMLElement/Roo.Element} el The context element
4734      * @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'})
4735      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4736      * @return {HTMLElement/Roo.Element} The new node or Element
4737      */
4738     insertBefore: function(el, values, returnElement){
4739         return this.doInsert('beforeBegin', el, values, returnElement);
4740     },
4741
4742     /**
4743      * Applies the supplied values to the template and inserts the new node(s) after el.
4744      * @param {String/HTMLElement/Roo.Element} el The context element
4745      * @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'})
4746      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4747      * @return {HTMLElement/Roo.Element} The new node or Element
4748      */
4749     insertAfter : function(el, values, returnElement){
4750         return this.doInsert('afterEnd', el, values, returnElement);
4751     },
4752     
4753     /**
4754      * Applies the supplied values to the template and appends the new node(s) to el.
4755      * @param {String/HTMLElement/Roo.Element} el The context element
4756      * @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'})
4757      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4758      * @return {HTMLElement/Roo.Element} The new node or Element
4759      */
4760     append : function(el, values, returnElement){
4761         return this.doInsert('beforeEnd', el, values, returnElement);
4762     },
4763
4764     doInsert : function(where, el, values, returnEl){
4765         el = Roo.getDom(el);
4766         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4767         return returnEl ? Roo.get(newNode, true) : newNode;
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @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'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     overwrite : function(el, values, returnElement){
4778         el = Roo.getDom(el);
4779         el.innerHTML = this.applyTemplate(values);
4780         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4781     }
4782 };
4783 /**
4784  * Alias for {@link #applyTemplate}
4785  * @method
4786  */
4787 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4788
4789 // backwards compat
4790 Roo.DomHelper.Template = Roo.Template;
4791
4792 /**
4793  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4794  * @param {String/HTMLElement} el A DOM element or its id
4795  * @returns {Roo.Template} The created template
4796  * @static
4797  */
4798 Roo.Template.from = function(el){
4799     el = Roo.getDom(el);
4800     return new Roo.Template(el.value || el.innerHTML);
4801 };/*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812
4813 /*
4814  * This is code is also distributed under MIT license for use
4815  * with jQuery and prototype JavaScript libraries.
4816  */
4817 /**
4818  * @class Roo.DomQuery
4819 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).
4820 <p>
4821 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>
4822
4823 <p>
4824 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.
4825 </p>
4826 <h4>Element Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>*</b> any element</li>
4829     <li> <b>E</b> an element with the tag E</li>
4830     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4831     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4832     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4833     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4834 </ul>
4835 <h4>Attribute Selectors:</h4>
4836 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4837 <ul class="list">
4838     <li> <b>E[foo]</b> has an attribute "foo"</li>
4839     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4840     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4841     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4842     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4843     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4844     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4845 </ul>
4846 <h4>Pseudo Classes:</h4>
4847 <ul class="list">
4848     <li> <b>E:first-child</b> E is the first child of its parent</li>
4849     <li> <b>E:last-child</b> E is the last child of its parent</li>
4850     <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>
4851     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4852     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4853     <li> <b>E:only-child</b> E is the only child of its parent</li>
4854     <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>
4855     <li> <b>E:first</b> the first E in the resultset</li>
4856     <li> <b>E:last</b> the last E in the resultset</li>
4857     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4858     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4859     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4860     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4861     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4862     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4863     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4864     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4865     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4866 </ul>
4867 <h4>CSS Value Selectors:</h4>
4868 <ul class="list">
4869     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4870     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4871     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4872     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4873     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4874     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4875 </ul>
4876  * @singleton
4877  */
4878 Roo.DomQuery = function(){
4879     var cache = {}, simpleCache = {}, valueCache = {};
4880     var nonSpace = /\S/;
4881     var trimRe = /^\s+|\s+$/g;
4882     var tplRe = /\{(\d+)\}/g;
4883     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4884     var tagTokenRe = /^(#)?([\w-\*]+)/;
4885     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4886
4887     function child(p, index){
4888         var i = 0;
4889         var n = p.firstChild;
4890         while(n){
4891             if(n.nodeType == 1){
4892                if(++i == index){
4893                    return n;
4894                }
4895             }
4896             n = n.nextSibling;
4897         }
4898         return null;
4899     };
4900
4901     function next(n){
4902         while((n = n.nextSibling) && n.nodeType != 1);
4903         return n;
4904     };
4905
4906     function prev(n){
4907         while((n = n.previousSibling) && n.nodeType != 1);
4908         return n;
4909     };
4910
4911     function children(d){
4912         var n = d.firstChild, ni = -1;
4913             while(n){
4914                 var nx = n.nextSibling;
4915                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4916                     d.removeChild(n);
4917                 }else{
4918                     n.nodeIndex = ++ni;
4919                 }
4920                 n = nx;
4921             }
4922             return this;
4923         };
4924
4925     function byClassName(c, a, v){
4926         if(!v){
4927             return c;
4928         }
4929         var r = [], ri = -1, cn;
4930         for(var i = 0, ci; ci = c[i]; i++){
4931             if((' '+ci.className+' ').indexOf(v) != -1){
4932                 r[++ri] = ci;
4933             }
4934         }
4935         return r;
4936     };
4937
4938     function attrValue(n, attr){
4939         if(!n.tagName && typeof n.length != "undefined"){
4940             n = n[0];
4941         }
4942         if(!n){
4943             return null;
4944         }
4945         if(attr == "for"){
4946             return n.htmlFor;
4947         }
4948         if(attr == "class" || attr == "className"){
4949             return n.className;
4950         }
4951         return n.getAttribute(attr) || n[attr];
4952
4953     };
4954
4955     function getNodes(ns, mode, tagName){
4956         var result = [], ri = -1, cs;
4957         if(!ns){
4958             return result;
4959         }
4960         tagName = tagName || "*";
4961         if(typeof ns.getElementsByTagName != "undefined"){
4962             ns = [ns];
4963         }
4964         if(!mode){
4965             for(var i = 0, ni; ni = ns[i]; i++){
4966                 cs = ni.getElementsByTagName(tagName);
4967                 for(var j = 0, ci; ci = cs[j]; j++){
4968                     result[++ri] = ci;
4969                 }
4970             }
4971         }else if(mode == "/" || mode == ">"){
4972             var utag = tagName.toUpperCase();
4973             for(var i = 0, ni, cn; ni = ns[i]; i++){
4974                 cn = ni.children || ni.childNodes;
4975                 for(var j = 0, cj; cj = cn[j]; j++){
4976                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4977                         result[++ri] = cj;
4978                     }
4979                 }
4980             }
4981         }else if(mode == "+"){
4982             var utag = tagName.toUpperCase();
4983             for(var i = 0, n; n = ns[i]; i++){
4984                 while((n = n.nextSibling) && n.nodeType != 1);
4985                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4986                     result[++ri] = n;
4987                 }
4988             }
4989         }else if(mode == "~"){
4990             for(var i = 0, n; n = ns[i]; i++){
4991                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4992                 if(n){
4993                     result[++ri] = n;
4994                 }
4995             }
4996         }
4997         return result;
4998     };
4999
5000     function concat(a, b){
5001         if(b.slice){
5002             return a.concat(b);
5003         }
5004         for(var i = 0, l = b.length; i < l; i++){
5005             a[a.length] = b[i];
5006         }
5007         return a;
5008     }
5009
5010     function byTag(cs, tagName){
5011         if(cs.tagName || cs == document){
5012             cs = [cs];
5013         }
5014         if(!tagName){
5015             return cs;
5016         }
5017         var r = [], ri = -1;
5018         tagName = tagName.toLowerCase();
5019         for(var i = 0, ci; ci = cs[i]; i++){
5020             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byId(cs, attr, id){
5028         if(cs.tagName || cs == document){
5029             cs = [cs];
5030         }
5031         if(!id){
5032             return cs;
5033         }
5034         var r = [], ri = -1;
5035         for(var i = 0,ci; ci = cs[i]; i++){
5036             if(ci && ci.id == id){
5037                 r[++ri] = ci;
5038                 return r;
5039             }
5040         }
5041         return r;
5042     };
5043
5044     function byAttribute(cs, attr, value, op, custom){
5045         var r = [], ri = -1, st = custom=="{";
5046         var f = Roo.DomQuery.operators[op];
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             var a;
5049             if(st){
5050                 a = Roo.DomQuery.getStyle(ci, attr);
5051             }
5052             else if(attr == "class" || attr == "className"){
5053                 a = ci.className;
5054             }else if(attr == "for"){
5055                 a = ci.htmlFor;
5056             }else if(attr == "href"){
5057                 a = ci.getAttribute("href", 2);
5058             }else{
5059                 a = ci.getAttribute(attr);
5060             }
5061             if((f && f(a, value)) || (!f && a)){
5062                 r[++ri] = ci;
5063             }
5064         }
5065         return r;
5066     };
5067
5068     function byPseudo(cs, name, value){
5069         return Roo.DomQuery.pseudos[name](cs, value);
5070     };
5071
5072     // This is for IE MSXML which does not support expandos.
5073     // IE runs the same speed using setAttribute, however FF slows way down
5074     // and Safari completely fails so they need to continue to use expandos.
5075     var isIE = window.ActiveXObject ? true : false;
5076
5077     // this eval is stop the compressor from
5078     // renaming the variable to something shorter
5079     
5080     /** eval:var:batch */
5081     var batch = 30803; 
5082
5083     var key = 30803;
5084
5085     function nodupIEXml(cs){
5086         var d = ++key;
5087         cs[0].setAttribute("_nodup", d);
5088         var r = [cs[0]];
5089         for(var i = 1, len = cs.length; i < len; i++){
5090             var c = cs[i];
5091             if(!c.getAttribute("_nodup") != d){
5092                 c.setAttribute("_nodup", d);
5093                 r[r.length] = c;
5094             }
5095         }
5096         for(var i = 0, len = cs.length; i < len; i++){
5097             cs[i].removeAttribute("_nodup");
5098         }
5099         return r;
5100     }
5101
5102     function nodup(cs){
5103         if(!cs){
5104             return [];
5105         }
5106         var len = cs.length, c, i, r = cs, cj, ri = -1;
5107         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5108             return cs;
5109         }
5110         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5111             return nodupIEXml(cs);
5112         }
5113         var d = ++key;
5114         cs[0]._nodup = d;
5115         for(i = 1; c = cs[i]; i++){
5116             if(c._nodup != d){
5117                 c._nodup = d;
5118             }else{
5119                 r = [];
5120                 for(var j = 0; j < i; j++){
5121                     r[++ri] = cs[j];
5122                 }
5123                 for(j = i+1; cj = cs[j]; j++){
5124                     if(cj._nodup != d){
5125                         cj._nodup = d;
5126                         r[++ri] = cj;
5127                     }
5128                 }
5129                 return r;
5130             }
5131         }
5132         return r;
5133     }
5134
5135     function quickDiffIEXml(c1, c2){
5136         var d = ++key;
5137         for(var i = 0, len = c1.length; i < len; i++){
5138             c1[i].setAttribute("_qdiff", d);
5139         }
5140         var r = [];
5141         for(var i = 0, len = c2.length; i < len; i++){
5142             if(c2[i].getAttribute("_qdiff") != d){
5143                 r[r.length] = c2[i];
5144             }
5145         }
5146         for(var i = 0, len = c1.length; i < len; i++){
5147            c1[i].removeAttribute("_qdiff");
5148         }
5149         return r;
5150     }
5151
5152     function quickDiff(c1, c2){
5153         var len1 = c1.length;
5154         if(!len1){
5155             return c2;
5156         }
5157         if(isIE && c1[0].selectSingleNode){
5158             return quickDiffIEXml(c1, c2);
5159         }
5160         var d = ++key;
5161         for(var i = 0; i < len1; i++){
5162             c1[i]._qdiff = d;
5163         }
5164         var r = [];
5165         for(var i = 0, len = c2.length; i < len; i++){
5166             if(c2[i]._qdiff != d){
5167                 r[r.length] = c2[i];
5168             }
5169         }
5170         return r;
5171     }
5172
5173     function quickId(ns, mode, root, id){
5174         if(ns == root){
5175            var d = root.ownerDocument || root;
5176            return d.getElementById(id);
5177         }
5178         ns = getNodes(ns, mode, "*");
5179         return byId(ns, null, id);
5180     }
5181
5182     return {
5183         getStyle : function(el, name){
5184             return Roo.fly(el).getStyle(name);
5185         },
5186         /**
5187          * Compiles a selector/xpath query into a reusable function. The returned function
5188          * takes one parameter "root" (optional), which is the context node from where the query should start.
5189          * @param {String} selector The selector/xpath query
5190          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5191          * @return {Function}
5192          */
5193         compile : function(path, type){
5194             type = type || "select";
5195             
5196             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5197             var q = path, mode, lq;
5198             var tk = Roo.DomQuery.matchers;
5199             var tklen = tk.length;
5200             var mm;
5201
5202             // accept leading mode switch
5203             var lmode = q.match(modeRe);
5204             if(lmode && lmode[1]){
5205                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5206                 q = q.replace(lmode[1], "");
5207             }
5208             // strip leading slashes
5209             while(path.substr(0, 1)=="/"){
5210                 path = path.substr(1);
5211             }
5212
5213             while(q && lq != q){
5214                 lq = q;
5215                 var tm = q.match(tagTokenRe);
5216                 if(type == "select"){
5217                     if(tm){
5218                         if(tm[1] == "#"){
5219                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5220                         }else{
5221                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5222                         }
5223                         q = q.replace(tm[0], "");
5224                     }else if(q.substr(0, 1) != '@'){
5225                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5226                     }
5227                 }else{
5228                     if(tm){
5229                         if(tm[1] == "#"){
5230                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5231                         }else{
5232                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5233                         }
5234                         q = q.replace(tm[0], "");
5235                     }
5236                 }
5237                 while(!(mm = q.match(modeRe))){
5238                     var matched = false;
5239                     for(var j = 0; j < tklen; j++){
5240                         var t = tk[j];
5241                         var m = q.match(t.re);
5242                         if(m){
5243                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5244                                                     return m[i];
5245                                                 });
5246                             q = q.replace(m[0], "");
5247                             matched = true;
5248                             break;
5249                         }
5250                     }
5251                     // prevent infinite loop on bad selector
5252                     if(!matched){
5253                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5254                     }
5255                 }
5256                 if(mm[1]){
5257                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5258                     q = q.replace(mm[1], "");
5259                 }
5260             }
5261             fn[fn.length] = "return nodup(n);\n}";
5262             
5263              /** 
5264               * list of variables that need from compression as they are used by eval.
5265              *  eval:var:batch 
5266              *  eval:var:nodup
5267              *  eval:var:byTag
5268              *  eval:var:ById
5269              *  eval:var:getNodes
5270              *  eval:var:quickId
5271              *  eval:var:mode
5272              *  eval:var:root
5273              *  eval:var:n
5274              *  eval:var:byClassName
5275              *  eval:var:byPseudo
5276              *  eval:var:byAttribute
5277              *  eval:var:attrValue
5278              * 
5279              **/ 
5280             eval(fn.join(""));
5281             return f;
5282         },
5283
5284         /**
5285          * Selects a group of elements.
5286          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5287          * @param {Node} root (optional) The start of the query (defaults to document).
5288          * @return {Array}
5289          */
5290         select : function(path, root, type){
5291             if(!root || root == document){
5292                 root = document;
5293             }
5294             if(typeof root == "string"){
5295                 root = document.getElementById(root);
5296             }
5297             var paths = path.split(",");
5298             var results = [];
5299             for(var i = 0, len = paths.length; i < len; i++){
5300                 var p = paths[i].replace(trimRe, "");
5301                 if(!cache[p]){
5302                     cache[p] = Roo.DomQuery.compile(p);
5303                     if(!cache[p]){
5304                         throw p + " is not a valid selector";
5305                     }
5306                 }
5307                 var result = cache[p](root);
5308                 if(result && result != document){
5309                     results = results.concat(result);
5310                 }
5311             }
5312             if(paths.length > 1){
5313                 return nodup(results);
5314             }
5315             return results;
5316         },
5317
5318         /**
5319          * Selects a single element.
5320          * @param {String} selector The selector/xpath query
5321          * @param {Node} root (optional) The start of the query (defaults to document).
5322          * @return {Element}
5323          */
5324         selectNode : function(path, root){
5325             return Roo.DomQuery.select(path, root)[0];
5326         },
5327
5328         /**
5329          * Selects the value of a node, optionally replacing null with the defaultValue.
5330          * @param {String} selector The selector/xpath query
5331          * @param {Node} root (optional) The start of the query (defaults to document).
5332          * @param {String} defaultValue
5333          */
5334         selectValue : function(path, root, defaultValue){
5335             path = path.replace(trimRe, "");
5336             if(!valueCache[path]){
5337                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5338             }
5339             var n = valueCache[path](root);
5340             n = n[0] ? n[0] : n;
5341             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5342             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5343         },
5344
5345         /**
5346          * Selects the value of a node, parsing integers and floats.
5347          * @param {String} selector The selector/xpath query
5348          * @param {Node} root (optional) The start of the query (defaults to document).
5349          * @param {Number} defaultValue
5350          * @return {Number}
5351          */
5352         selectNumber : function(path, root, defaultValue){
5353             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5354             return parseFloat(v);
5355         },
5356
5357         /**
5358          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5359          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5360          * @param {String} selector The simple selector to test
5361          * @return {Boolean}
5362          */
5363         is : function(el, ss){
5364             if(typeof el == "string"){
5365                 el = document.getElementById(el);
5366             }
5367             var isArray = (el instanceof Array);
5368             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5369             return isArray ? (result.length == el.length) : (result.length > 0);
5370         },
5371
5372         /**
5373          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5374          * @param {Array} el An array of elements to filter
5375          * @param {String} selector The simple selector to test
5376          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5377          * the selector instead of the ones that match
5378          * @return {Array}
5379          */
5380         filter : function(els, ss, nonMatches){
5381             ss = ss.replace(trimRe, "");
5382             if(!simpleCache[ss]){
5383                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5384             }
5385             var result = simpleCache[ss](els);
5386             return nonMatches ? quickDiff(result, els) : result;
5387         },
5388
5389         /**
5390          * Collection of matching regular expressions and code snippets.
5391          */
5392         matchers : [{
5393                 re: /^\.([\w-]+)/,
5394                 select: 'n = byClassName(n, null, " {1} ");'
5395             }, {
5396                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5397                 select: 'n = byPseudo(n, "{1}", "{2}");'
5398             },{
5399                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5400                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5401             }, {
5402                 re: /^#([\w-]+)/,
5403                 select: 'n = byId(n, null, "{1}");'
5404             },{
5405                 re: /^@([\w-]+)/,
5406                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5407             }
5408         ],
5409
5410         /**
5411          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5412          * 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;.
5413          */
5414         operators : {
5415             "=" : function(a, v){
5416                 return a == v;
5417             },
5418             "!=" : function(a, v){
5419                 return a != v;
5420             },
5421             "^=" : function(a, v){
5422                 return a && a.substr(0, v.length) == v;
5423             },
5424             "$=" : function(a, v){
5425                 return a && a.substr(a.length-v.length) == v;
5426             },
5427             "*=" : function(a, v){
5428                 return a && a.indexOf(v) !== -1;
5429             },
5430             "%=" : function(a, v){
5431                 return (a % v) == 0;
5432             },
5433             "|=" : function(a, v){
5434                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5435             },
5436             "~=" : function(a, v){
5437                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5438             }
5439         },
5440
5441         /**
5442          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5443          * and the argument (if any) supplied in the selector.
5444          */
5445         pseudos : {
5446             "first-child" : function(c){
5447                 var r = [], ri = -1, n;
5448                 for(var i = 0, ci; ci = n = c[i]; i++){
5449                     while((n = n.previousSibling) && n.nodeType != 1);
5450                     if(!n){
5451                         r[++ri] = ci;
5452                     }
5453                 }
5454                 return r;
5455             },
5456
5457             "last-child" : function(c){
5458                 var r = [], ri = -1, n;
5459                 for(var i = 0, ci; ci = n = c[i]; i++){
5460                     while((n = n.nextSibling) && n.nodeType != 1);
5461                     if(!n){
5462                         r[++ri] = ci;
5463                     }
5464                 }
5465                 return r;
5466             },
5467
5468             "nth-child" : function(c, a) {
5469                 var r = [], ri = -1;
5470                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5471                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5472                 for(var i = 0, n; n = c[i]; i++){
5473                     var pn = n.parentNode;
5474                     if (batch != pn._batch) {
5475                         var j = 0;
5476                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5477                             if(cn.nodeType == 1){
5478                                cn.nodeIndex = ++j;
5479                             }
5480                         }
5481                         pn._batch = batch;
5482                     }
5483                     if (f == 1) {
5484                         if (l == 0 || n.nodeIndex == l){
5485                             r[++ri] = n;
5486                         }
5487                     } else if ((n.nodeIndex + l) % f == 0){
5488                         r[++ri] = n;
5489                     }
5490                 }
5491
5492                 return r;
5493             },
5494
5495             "only-child" : function(c){
5496                 var r = [], ri = -1;;
5497                 for(var i = 0, ci; ci = c[i]; i++){
5498                     if(!prev(ci) && !next(ci)){
5499                         r[++ri] = ci;
5500                     }
5501                 }
5502                 return r;
5503             },
5504
5505             "empty" : function(c){
5506                 var r = [], ri = -1;
5507                 for(var i = 0, ci; ci = c[i]; i++){
5508                     var cns = ci.childNodes, j = 0, cn, empty = true;
5509                     while(cn = cns[j]){
5510                         ++j;
5511                         if(cn.nodeType == 1 || cn.nodeType == 3){
5512                             empty = false;
5513                             break;
5514                         }
5515                     }
5516                     if(empty){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             },
5522
5523             "contains" : function(c, v){
5524                 var r = [], ri = -1;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "nodeValue" : function(c, v){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5537                         r[++ri] = ci;
5538                     }
5539                 }
5540                 return r;
5541             },
5542
5543             "checked" : function(c){
5544                 var r = [], ri = -1;
5545                 for(var i = 0, ci; ci = c[i]; i++){
5546                     if(ci.checked == true){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "not" : function(c, ss){
5554                 return Roo.DomQuery.filter(c, ss, true);
5555             },
5556
5557             "odd" : function(c){
5558                 return this["nth-child"](c, "odd");
5559             },
5560
5561             "even" : function(c){
5562                 return this["nth-child"](c, "even");
5563             },
5564
5565             "nth" : function(c, a){
5566                 return c[a-1] || [];
5567             },
5568
5569             "first" : function(c){
5570                 return c[0] || [];
5571             },
5572
5573             "last" : function(c){
5574                 return c[c.length-1] || [];
5575             },
5576
5577             "has" : function(c, ss){
5578                 var s = Roo.DomQuery.select;
5579                 var r = [], ri = -1;
5580                 for(var i = 0, ci; ci = c[i]; i++){
5581                     if(s(ss, ci).length > 0){
5582                         r[++ri] = ci;
5583                     }
5584                 }
5585                 return r;
5586             },
5587
5588             "next" : function(c, ss){
5589                 var is = Roo.DomQuery.is;
5590                 var r = [], ri = -1;
5591                 for(var i = 0, ci; ci = c[i]; i++){
5592                     var n = next(ci);
5593                     if(n && is(n, ss)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "prev" : function(c, ss){
5601                 var is = Roo.DomQuery.is;
5602                 var r = [], ri = -1;
5603                 for(var i = 0, ci; ci = c[i]; i++){
5604                     var n = prev(ci);
5605                     if(n && is(n, ss)){
5606                         r[++ri] = ci;
5607                     }
5608                 }
5609                 return r;
5610             }
5611         }
5612     };
5613 }();
5614
5615 /**
5616  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5617  * @param {String} path The selector/xpath query
5618  * @param {Node} root (optional) The start of the query (defaults to document).
5619  * @return {Array}
5620  * @member Roo
5621  * @method query
5622  */
5623 Roo.query = Roo.DomQuery.select;
5624 /*
5625  * Based on:
5626  * Ext JS Library 1.1.1
5627  * Copyright(c) 2006-2007, Ext JS, LLC.
5628  *
5629  * Originally Released Under LGPL - original licence link has changed is not relivant.
5630  *
5631  * Fork - LGPL
5632  * <script type="text/javascript">
5633  */
5634
5635 /**
5636  * @class Roo.util.Observable
5637  * Base class that provides a common interface for publishing events. Subclasses are expected to
5638  * to have a property "events" with all the events defined.<br>
5639  * For example:
5640  * <pre><code>
5641  Employee = function(name){
5642     this.name = name;
5643     this.addEvents({
5644         "fired" : true,
5645         "quit" : true
5646     });
5647  }
5648  Roo.extend(Employee, Roo.util.Observable);
5649 </code></pre>
5650  * @param {Object} config properties to use (incuding events / listeners)
5651  */
5652
5653 Roo.util.Observable = function(cfg){
5654     
5655     cfg = cfg|| {};
5656     this.addEvents(cfg.events || {});
5657     if (cfg.events) {
5658         delete cfg.events; // make sure
5659     }
5660      
5661     Roo.apply(this, cfg);
5662     
5663     if(this.listeners){
5664         this.on(this.listeners);
5665         delete this.listeners;
5666     }
5667 };
5668 Roo.util.Observable.prototype = {
5669     /** 
5670  * @cfg {Object} listeners  list of events and functions to call for this object, 
5671  * For example :
5672  * <pre><code>
5673     listeners :  { 
5674        'click' : function(e) {
5675            ..... 
5676         } ,
5677         .... 
5678     } 
5679   </code></pre>
5680  */
5681     
5682     
5683     /**
5684      * Fires the specified event with the passed parameters (minus the event name).
5685      * @param {String} eventName
5686      * @param {Object...} args Variable number of parameters are passed to handlers
5687      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5688      */
5689     fireEvent : function(){
5690         var ce = this.events[arguments[0].toLowerCase()];
5691         if(typeof ce == "object"){
5692             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5693         }else{
5694             return true;
5695         }
5696     },
5697
5698     // private
5699     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5700
5701     /**
5702      * Appends an event handler to this component
5703      * @param {String}   eventName The type of event to listen for
5704      * @param {Function} handler The method the event invokes
5705      * @param {Object}   scope (optional) The scope in which to execute the handler
5706      * function. The handler function's "this" context.
5707      * @param {Object}   options (optional) An object containing handler configuration
5708      * properties. This may contain any of the following properties:<ul>
5709      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5710      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5711      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5712      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5713      * by the specified number of milliseconds. If the event fires again within that time, the original
5714      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5715      * </ul><br>
5716      * <p>
5717      * <b>Combining Options</b><br>
5718      * Using the options argument, it is possible to combine different types of listeners:<br>
5719      * <br>
5720      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5721                 <pre><code>
5722                 el.on('click', this.onClick, this, {
5723                         single: true,
5724                 delay: 100,
5725                 forumId: 4
5726                 });
5727                 </code></pre>
5728      * <p>
5729      * <b>Attaching multiple handlers in 1 call</b><br>
5730      * The method also allows for a single argument to be passed which is a config object containing properties
5731      * which specify multiple handlers.
5732      * <pre><code>
5733                 el.on({
5734                         'click': {
5735                         fn: this.onClick,
5736                         scope: this,
5737                         delay: 100
5738                 }, 
5739                 'mouseover': {
5740                         fn: this.onMouseOver,
5741                         scope: this
5742                 },
5743                 'mouseout': {
5744                         fn: this.onMouseOut,
5745                         scope: this
5746                 }
5747                 });
5748                 </code></pre>
5749      * <p>
5750      * Or a shorthand syntax which passes the same scope object to all handlers:
5751         <pre><code>
5752                 el.on({
5753                         'click': this.onClick,
5754                 'mouseover': this.onMouseOver,
5755                 'mouseout': this.onMouseOut,
5756                 scope: this
5757                 });
5758                 </code></pre>
5759      */
5760     addListener : function(eventName, fn, scope, o){
5761         if(typeof eventName == "object"){
5762             o = eventName;
5763             for(var e in o){
5764                 if(this.filterOptRe.test(e)){
5765                     continue;
5766                 }
5767                 if(typeof o[e] == "function"){
5768                     // shared options
5769                     this.addListener(e, o[e], o.scope,  o);
5770                 }else{
5771                     // individual options
5772                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5773                 }
5774             }
5775             return;
5776         }
5777         o = (!o || typeof o == "boolean") ? {} : o;
5778         eventName = eventName.toLowerCase();
5779         var ce = this.events[eventName] || true;
5780         if(typeof ce == "boolean"){
5781             ce = new Roo.util.Event(this, eventName);
5782             this.events[eventName] = ce;
5783         }
5784         ce.addListener(fn, scope, o);
5785     },
5786
5787     /**
5788      * Removes a listener
5789      * @param {String}   eventName     The type of event to listen for
5790      * @param {Function} handler        The handler to remove
5791      * @param {Object}   scope  (optional) The scope (this object) for the handler
5792      */
5793     removeListener : function(eventName, fn, scope){
5794         var ce = this.events[eventName.toLowerCase()];
5795         if(typeof ce == "object"){
5796             ce.removeListener(fn, scope);
5797         }
5798     },
5799
5800     /**
5801      * Removes all listeners for this object
5802      */
5803     purgeListeners : function(){
5804         for(var evt in this.events){
5805             if(typeof this.events[evt] == "object"){
5806                  this.events[evt].clearListeners();
5807             }
5808         }
5809     },
5810
5811     relayEvents : function(o, events){
5812         var createHandler = function(ename){
5813             return function(){
5814                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5815             };
5816         };
5817         for(var i = 0, len = events.length; i < len; i++){
5818             var ename = events[i];
5819             if(!this.events[ename]){ this.events[ename] = true; };
5820             o.on(ename, createHandler(ename), this);
5821         }
5822     },
5823
5824     /**
5825      * Used to define events on this Observable
5826      * @param {Object} object The object with the events defined
5827      */
5828     addEvents : function(o){
5829         if(!this.events){
5830             this.events = {};
5831         }
5832         Roo.applyIf(this.events, o);
5833     },
5834
5835     /**
5836      * Checks to see if this object has any listeners for a specified event
5837      * @param {String} eventName The name of the event to check for
5838      * @return {Boolean} True if the event is being listened for, else false
5839      */
5840     hasListener : function(eventName){
5841         var e = this.events[eventName];
5842         return typeof e == "object" && e.listeners.length > 0;
5843     }
5844 };
5845 /**
5846  * Appends an event handler to this element (shorthand for addListener)
5847  * @param {String}   eventName     The type of event to listen for
5848  * @param {Function} handler        The method the event invokes
5849  * @param {Object}   scope (optional) The scope in which to execute the handler
5850  * function. The handler function's "this" context.
5851  * @param {Object}   options  (optional)
5852  * @method
5853  */
5854 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5855 /**
5856  * Removes a listener (shorthand for removeListener)
5857  * @param {String}   eventName     The type of event to listen for
5858  * @param {Function} handler        The handler to remove
5859  * @param {Object}   scope  (optional) The scope (this object) for the handler
5860  * @method
5861  */
5862 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5863
5864 /**
5865  * Starts capture on the specified Observable. All events will be passed
5866  * to the supplied function with the event name + standard signature of the event
5867  * <b>before</b> the event is fired. If the supplied function returns false,
5868  * the event will not fire.
5869  * @param {Observable} o The Observable to capture
5870  * @param {Function} fn The function to call
5871  * @param {Object} scope (optional) The scope (this object) for the fn
5872  * @static
5873  */
5874 Roo.util.Observable.capture = function(o, fn, scope){
5875     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5876 };
5877
5878 /**
5879  * Removes <b>all</b> added captures from the Observable.
5880  * @param {Observable} o The Observable to release
5881  * @static
5882  */
5883 Roo.util.Observable.releaseCapture = function(o){
5884     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5885 };
5886
5887 (function(){
5888
5889     var createBuffered = function(h, o, scope){
5890         var task = new Roo.util.DelayedTask();
5891         return function(){
5892             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5893         };
5894     };
5895
5896     var createSingle = function(h, e, fn, scope){
5897         return function(){
5898             e.removeListener(fn, scope);
5899             return h.apply(scope, arguments);
5900         };
5901     };
5902
5903     var createDelayed = function(h, o, scope){
5904         return function(){
5905             var args = Array.prototype.slice.call(arguments, 0);
5906             setTimeout(function(){
5907                 h.apply(scope, args);
5908             }, o.delay || 10);
5909         };
5910     };
5911
5912     Roo.util.Event = function(obj, name){
5913         this.name = name;
5914         this.obj = obj;
5915         this.listeners = [];
5916     };
5917
5918     Roo.util.Event.prototype = {
5919         addListener : function(fn, scope, options){
5920             var o = options || {};
5921             scope = scope || this.obj;
5922             if(!this.isListening(fn, scope)){
5923                 var l = {fn: fn, scope: scope, options: o};
5924                 var h = fn;
5925                 if(o.delay){
5926                     h = createDelayed(h, o, scope);
5927                 }
5928                 if(o.single){
5929                     h = createSingle(h, this, fn, scope);
5930                 }
5931                 if(o.buffer){
5932                     h = createBuffered(h, o, scope);
5933                 }
5934                 l.fireFn = h;
5935                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5936                     this.listeners.push(l);
5937                 }else{
5938                     this.listeners = this.listeners.slice(0);
5939                     this.listeners.push(l);
5940                 }
5941             }
5942         },
5943
5944         findListener : function(fn, scope){
5945             scope = scope || this.obj;
5946             var ls = this.listeners;
5947             for(var i = 0, len = ls.length; i < len; i++){
5948                 var l = ls[i];
5949                 if(l.fn == fn && l.scope == scope){
5950                     return i;
5951                 }
5952             }
5953             return -1;
5954         },
5955
5956         isListening : function(fn, scope){
5957             return this.findListener(fn, scope) != -1;
5958         },
5959
5960         removeListener : function(fn, scope){
5961             var index;
5962             if((index = this.findListener(fn, scope)) != -1){
5963                 if(!this.firing){
5964                     this.listeners.splice(index, 1);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.splice(index, 1);
5968                 }
5969                 return true;
5970             }
5971             return false;
5972         },
5973
5974         clearListeners : function(){
5975             this.listeners = [];
5976         },
5977
5978         fire : function(){
5979             var ls = this.listeners, scope, len = ls.length;
5980             if(len > 0){
5981                 this.firing = true;
5982                 var args = Array.prototype.slice.call(arguments, 0);
5983                 for(var i = 0; i < len; i++){
5984                     var l = ls[i];
5985                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5986                         this.firing = false;
5987                         return false;
5988                     }
5989                 }
5990                 this.firing = false;
5991             }
5992             return true;
5993         }
5994     };
5995 })();/*
5996  * Based on:
5997  * Ext JS Library 1.1.1
5998  * Copyright(c) 2006-2007, Ext JS, LLC.
5999  *
6000  * Originally Released Under LGPL - original licence link has changed is not relivant.
6001  *
6002  * Fork - LGPL
6003  * <script type="text/javascript">
6004  */
6005
6006 /**
6007  * @class Roo.EventManager
6008  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6009  * several useful events directly.
6010  * See {@link Roo.EventObject} for more details on normalized event objects.
6011  * @singleton
6012  */
6013 Roo.EventManager = function(){
6014     var docReadyEvent, docReadyProcId, docReadyState = false;
6015     var resizeEvent, resizeTask, textEvent, textSize;
6016     var E = Roo.lib.Event;
6017     var D = Roo.lib.Dom;
6018
6019
6020     var fireDocReady = function(){
6021         if(!docReadyState){
6022             docReadyState = true;
6023             Roo.isReady = true;
6024             if(docReadyProcId){
6025                 clearInterval(docReadyProcId);
6026             }
6027             if(Roo.isGecko || Roo.isOpera) {
6028                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6029             }
6030             if(Roo.isIE){
6031                 var defer = document.getElementById("ie-deferred-loader");
6032                 if(defer){
6033                     defer.onreadystatechange = null;
6034                     defer.parentNode.removeChild(defer);
6035                 }
6036             }
6037             if(docReadyEvent){
6038                 docReadyEvent.fire();
6039                 docReadyEvent.clearListeners();
6040             }
6041         }
6042     };
6043     
6044     var initDocReady = function(){
6045         docReadyEvent = new Roo.util.Event();
6046         if(Roo.isGecko || Roo.isOpera) {
6047             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6048         }else if(Roo.isIE){
6049             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6050             var defer = document.getElementById("ie-deferred-loader");
6051             defer.onreadystatechange = function(){
6052                 if(this.readyState == "complete"){
6053                     fireDocReady();
6054                 }
6055             };
6056         }else if(Roo.isSafari){ 
6057             docReadyProcId = setInterval(function(){
6058                 var rs = document.readyState;
6059                 if(rs == "complete") {
6060                     fireDocReady();     
6061                  }
6062             }, 10);
6063         }
6064         // no matter what, make sure it fires on load
6065         E.on(window, "load", fireDocReady);
6066     };
6067
6068     var createBuffered = function(h, o){
6069         var task = new Roo.util.DelayedTask(h);
6070         return function(e){
6071             // create new event object impl so new events don't wipe out properties
6072             e = new Roo.EventObjectImpl(e);
6073             task.delay(o.buffer, h, null, [e]);
6074         };
6075     };
6076
6077     var createSingle = function(h, el, ename, fn){
6078         return function(e){
6079             Roo.EventManager.removeListener(el, ename, fn);
6080             h(e);
6081         };
6082     };
6083
6084     var createDelayed = function(h, o){
6085         return function(e){
6086             // create new event object impl so new events don't wipe out properties
6087             e = new Roo.EventObjectImpl(e);
6088             setTimeout(function(){
6089                 h(e);
6090             }, o.delay || 10);
6091         };
6092     };
6093
6094     var listen = function(element, ename, opt, fn, scope){
6095         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6096         fn = fn || o.fn; scope = scope || o.scope;
6097         var el = Roo.getDom(element);
6098         if(!el){
6099             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6100         }
6101         var h = function(e){
6102             e = Roo.EventObject.setEvent(e);
6103             var t;
6104             if(o.delegate){
6105                 t = e.getTarget(o.delegate, el);
6106                 if(!t){
6107                     return;
6108                 }
6109             }else{
6110                 t = e.target;
6111             }
6112             if(o.stopEvent === true){
6113                 e.stopEvent();
6114             }
6115             if(o.preventDefault === true){
6116                e.preventDefault();
6117             }
6118             if(o.stopPropagation === true){
6119                 e.stopPropagation();
6120             }
6121
6122             if(o.normalized === false){
6123                 e = e.browserEvent;
6124             }
6125
6126             fn.call(scope || el, e, t, o);
6127         };
6128         if(o.delay){
6129             h = createDelayed(h, o);
6130         }
6131         if(o.single){
6132             h = createSingle(h, el, ename, fn);
6133         }
6134         if(o.buffer){
6135             h = createBuffered(h, o);
6136         }
6137         fn._handlers = fn._handlers || [];
6138         fn._handlers.push([Roo.id(el), ename, h]);
6139
6140         E.on(el, ename, h);
6141         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6142             el.addEventListener("DOMMouseScroll", h, false);
6143             E.on(window, 'unload', function(){
6144                 el.removeEventListener("DOMMouseScroll", h, false);
6145             });
6146         }
6147         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6148             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6149         }
6150         return h;
6151     };
6152
6153     var stopListening = function(el, ename, fn){
6154         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6155         if(hds){
6156             for(var i = 0, len = hds.length; i < len; i++){
6157                 var h = hds[i];
6158                 if(h[0] == id && h[1] == ename){
6159                     hd = h[2];
6160                     hds.splice(i, 1);
6161                     break;
6162                 }
6163             }
6164         }
6165         E.un(el, ename, hd);
6166         el = Roo.getDom(el);
6167         if(ename == "mousewheel" && el.addEventListener){
6168             el.removeEventListener("DOMMouseScroll", hd, false);
6169         }
6170         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6171             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6172         }
6173     };
6174
6175     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6176     
6177     var pub = {
6178         
6179         
6180         /** 
6181          * Fix for doc tools
6182          * @scope Roo.EventManager
6183          */
6184         
6185         
6186         /** 
6187          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6188          * object with a Roo.EventObject
6189          * @param {Function} fn        The method the event invokes
6190          * @param {Object}   scope    An object that becomes the scope of the handler
6191          * @param {boolean}  override If true, the obj passed in becomes
6192          *                             the execution scope of the listener
6193          * @return {Function} The wrapped function
6194          * @deprecated
6195          */
6196         wrap : function(fn, scope, override){
6197             return function(e){
6198                 Roo.EventObject.setEvent(e);
6199                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6200             };
6201         },
6202         
6203         /**
6204      * Appends an event handler to an element (shorthand for addListener)
6205      * @param {String/HTMLElement}   element        The html element or id to assign the
6206      * @param {String}   eventName The type of event to listen for
6207      * @param {Function} handler The method the event invokes
6208      * @param {Object}   scope (optional) The scope in which to execute the handler
6209      * function. The handler function's "this" context.
6210      * @param {Object}   options (optional) An object containing handler configuration
6211      * properties. This may contain any of the following properties:<ul>
6212      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6213      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6214      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6215      * <li>preventDefault {Boolean} True to prevent the default action</li>
6216      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6217      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6218      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6219      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6220      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6221      * by the specified number of milliseconds. If the event fires again within that time, the original
6222      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6223      * </ul><br>
6224      * <p>
6225      * <b>Combining Options</b><br>
6226      * Using the options argument, it is possible to combine different types of listeners:<br>
6227      * <br>
6228      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6229      * Code:<pre><code>
6230 el.on('click', this.onClick, this, {
6231     single: true,
6232     delay: 100,
6233     stopEvent : true,
6234     forumId: 4
6235 });</code></pre>
6236      * <p>
6237      * <b>Attaching multiple handlers in 1 call</b><br>
6238       * The method also allows for a single argument to be passed which is a config object containing properties
6239      * which specify multiple handlers.
6240      * <p>
6241      * Code:<pre><code>
6242 el.on({
6243     'click' : {
6244         fn: this.onClick
6245         scope: this,
6246         delay: 100
6247     },
6248     'mouseover' : {
6249         fn: this.onMouseOver
6250         scope: this
6251     },
6252     'mouseout' : {
6253         fn: this.onMouseOut
6254         scope: this
6255     }
6256 });</code></pre>
6257      * <p>
6258      * Or a shorthand syntax:<br>
6259      * Code:<pre><code>
6260 el.on({
6261     'click' : this.onClick,
6262     'mouseover' : this.onMouseOver,
6263     'mouseout' : this.onMouseOut
6264     scope: this
6265 });</code></pre>
6266      */
6267         addListener : function(element, eventName, fn, scope, options){
6268             if(typeof eventName == "object"){
6269                 var o = eventName;
6270                 for(var e in o){
6271                     if(propRe.test(e)){
6272                         continue;
6273                     }
6274                     if(typeof o[e] == "function"){
6275                         // shared options
6276                         listen(element, e, o, o[e], o.scope);
6277                     }else{
6278                         // individual options
6279                         listen(element, e, o[e]);
6280                     }
6281                 }
6282                 return;
6283             }
6284             return listen(element, eventName, options, fn, scope);
6285         },
6286         
6287         /**
6288          * Removes an event handler
6289          *
6290          * @param {String/HTMLElement}   element        The id or html element to remove the 
6291          *                             event from
6292          * @param {String}   eventName     The type of event
6293          * @param {Function} fn
6294          * @return {Boolean} True if a listener was actually removed
6295          */
6296         removeListener : function(element, eventName, fn){
6297             return stopListening(element, eventName, fn);
6298         },
6299         
6300         /**
6301          * Fires when the document is ready (before onload and before images are loaded). Can be 
6302          * accessed shorthanded Roo.onReady().
6303          * @param {Function} fn        The method the event invokes
6304          * @param {Object}   scope    An  object that becomes the scope of the handler
6305          * @param {boolean}  options
6306          */
6307         onDocumentReady : function(fn, scope, options){
6308             if(docReadyState){ // if it already fired
6309                 docReadyEvent.addListener(fn, scope, options);
6310                 docReadyEvent.fire();
6311                 docReadyEvent.clearListeners();
6312                 return;
6313             }
6314             if(!docReadyEvent){
6315                 initDocReady();
6316             }
6317             docReadyEvent.addListener(fn, scope, options);
6318         },
6319         
6320         /**
6321          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6322          * @param {Function} fn        The method the event invokes
6323          * @param {Object}   scope    An object that becomes the scope of the handler
6324          * @param {boolean}  options
6325          */
6326         onWindowResize : function(fn, scope, options){
6327             if(!resizeEvent){
6328                 resizeEvent = new Roo.util.Event();
6329                 resizeTask = new Roo.util.DelayedTask(function(){
6330                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6331                 });
6332                 E.on(window, "resize", function(){
6333                     if(Roo.isIE){
6334                         resizeTask.delay(50);
6335                     }else{
6336                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6337                     }
6338                 });
6339             }
6340             resizeEvent.addListener(fn, scope, options);
6341         },
6342
6343         /**
6344          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6345          * @param {Function} fn        The method the event invokes
6346          * @param {Object}   scope    An object that becomes the scope of the handler
6347          * @param {boolean}  options
6348          */
6349         onTextResize : function(fn, scope, options){
6350             if(!textEvent){
6351                 textEvent = new Roo.util.Event();
6352                 var textEl = new Roo.Element(document.createElement('div'));
6353                 textEl.dom.className = 'x-text-resize';
6354                 textEl.dom.innerHTML = 'X';
6355                 textEl.appendTo(document.body);
6356                 textSize = textEl.dom.offsetHeight;
6357                 setInterval(function(){
6358                     if(textEl.dom.offsetHeight != textSize){
6359                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6360                     }
6361                 }, this.textResizeInterval);
6362             }
6363             textEvent.addListener(fn, scope, options);
6364         },
6365
6366         /**
6367          * Removes the passed window resize listener.
6368          * @param {Function} fn        The method the event invokes
6369          * @param {Object}   scope    The scope of handler
6370          */
6371         removeResizeListener : function(fn, scope){
6372             if(resizeEvent){
6373                 resizeEvent.removeListener(fn, scope);
6374             }
6375         },
6376
6377         // private
6378         fireResize : function(){
6379             if(resizeEvent){
6380                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6381             }   
6382         },
6383         /**
6384          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6385          */
6386         ieDeferSrc : false,
6387         /**
6388          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6389          */
6390         textResizeInterval : 50
6391     };
6392     
6393     /**
6394      * Fix for doc tools
6395      * @scopeAlias pub=Roo.EventManager
6396      */
6397     
6398      /**
6399      * Appends an event handler to an element (shorthand for addListener)
6400      * @param {String/HTMLElement}   element        The html element or id to assign the
6401      * @param {String}   eventName The type of event to listen for
6402      * @param {Function} handler The method the event invokes
6403      * @param {Object}   scope (optional) The scope in which to execute the handler
6404      * function. The handler function's "this" context.
6405      * @param {Object}   options (optional) An object containing handler configuration
6406      * properties. This may contain any of the following properties:<ul>
6407      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6408      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6409      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6410      * <li>preventDefault {Boolean} True to prevent the default action</li>
6411      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6412      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6413      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6414      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6415      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6416      * by the specified number of milliseconds. If the event fires again within that time, the original
6417      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6418      * </ul><br>
6419      * <p>
6420      * <b>Combining Options</b><br>
6421      * Using the options argument, it is possible to combine different types of listeners:<br>
6422      * <br>
6423      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6424      * Code:<pre><code>
6425 el.on('click', this.onClick, this, {
6426     single: true,
6427     delay: 100,
6428     stopEvent : true,
6429     forumId: 4
6430 });</code></pre>
6431      * <p>
6432      * <b>Attaching multiple handlers in 1 call</b><br>
6433       * The method also allows for a single argument to be passed which is a config object containing properties
6434      * which specify multiple handlers.
6435      * <p>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : {
6439         fn: this.onClick
6440         scope: this,
6441         delay: 100
6442     },
6443     'mouseover' : {
6444         fn: this.onMouseOver
6445         scope: this
6446     },
6447     'mouseout' : {
6448         fn: this.onMouseOut
6449         scope: this
6450     }
6451 });</code></pre>
6452      * <p>
6453      * Or a shorthand syntax:<br>
6454      * Code:<pre><code>
6455 el.on({
6456     'click' : this.onClick,
6457     'mouseover' : this.onMouseOver,
6458     'mouseout' : this.onMouseOut
6459     scope: this
6460 });</code></pre>
6461      */
6462     pub.on = pub.addListener;
6463     pub.un = pub.removeListener;
6464
6465     pub.stoppedMouseDownEvent = new Roo.util.Event();
6466     return pub;
6467 }();
6468 /**
6469   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6470   * @param {Function} fn        The method the event invokes
6471   * @param {Object}   scope    An  object that becomes the scope of the handler
6472   * @param {boolean}  override If true, the obj passed in becomes
6473   *                             the execution scope of the listener
6474   * @member Roo
6475   * @method onReady
6476  */
6477 Roo.onReady = Roo.EventManager.onDocumentReady;
6478
6479 Roo.onReady(function(){
6480     var bd = Roo.get(document.body);
6481     if(!bd){ return; }
6482
6483     var cls = [
6484             Roo.isIE ? "roo-ie"
6485             : Roo.isGecko ? "roo-gecko"
6486             : Roo.isOpera ? "roo-opera"
6487             : Roo.isSafari ? "roo-safari" : ""];
6488
6489     if(Roo.isMac){
6490         cls.push("roo-mac");
6491     }
6492     if(Roo.isLinux){
6493         cls.push("roo-linux");
6494     }
6495     if(Roo.isBorderBox){
6496         cls.push('roo-border-box');
6497     }
6498     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6499         var p = bd.dom.parentNode;
6500         if(p){
6501             p.className += ' roo-strict';
6502         }
6503     }
6504     bd.addClass(cls.join(' '));
6505 });
6506
6507 /**
6508  * @class Roo.EventObject
6509  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6510  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6511  * Example:
6512  * <pre><code>
6513  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6514     e.preventDefault();
6515     var target = e.getTarget();
6516     ...
6517  }
6518  var myDiv = Roo.get("myDiv");
6519  myDiv.on("click", handleClick);
6520  //or
6521  Roo.EventManager.on("myDiv", 'click', handleClick);
6522  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6523  </code></pre>
6524  * @singleton
6525  */
6526 Roo.EventObject = function(){
6527     
6528     var E = Roo.lib.Event;
6529     
6530     // safari keypress events for special keys return bad keycodes
6531     var safariKeys = {
6532         63234 : 37, // left
6533         63235 : 39, // right
6534         63232 : 38, // up
6535         63233 : 40, // down
6536         63276 : 33, // page up
6537         63277 : 34, // page down
6538         63272 : 46, // delete
6539         63273 : 36, // home
6540         63275 : 35  // end
6541     };
6542
6543     // normalize button clicks
6544     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6545                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6546
6547     Roo.EventObjectImpl = function(e){
6548         if(e){
6549             this.setEvent(e.browserEvent || e);
6550         }
6551     };
6552     Roo.EventObjectImpl.prototype = {
6553         /**
6554          * Used to fix doc tools.
6555          * @scope Roo.EventObject.prototype
6556          */
6557             
6558
6559         
6560         
6561         /** The normal browser event */
6562         browserEvent : null,
6563         /** The button pressed in a mouse event */
6564         button : -1,
6565         /** True if the shift key was down during the event */
6566         shiftKey : false,
6567         /** True if the control key was down during the event */
6568         ctrlKey : false,
6569         /** True if the alt key was down during the event */
6570         altKey : false,
6571
6572         /** Key constant 
6573         * @type Number */
6574         BACKSPACE : 8,
6575         /** Key constant 
6576         * @type Number */
6577         TAB : 9,
6578         /** Key constant 
6579         * @type Number */
6580         RETURN : 13,
6581         /** Key constant 
6582         * @type Number */
6583         ENTER : 13,
6584         /** Key constant 
6585         * @type Number */
6586         SHIFT : 16,
6587         /** Key constant 
6588         * @type Number */
6589         CONTROL : 17,
6590         /** Key constant 
6591         * @type Number */
6592         ESC : 27,
6593         /** Key constant 
6594         * @type Number */
6595         SPACE : 32,
6596         /** Key constant 
6597         * @type Number */
6598         PAGEUP : 33,
6599         /** Key constant 
6600         * @type Number */
6601         PAGEDOWN : 34,
6602         /** Key constant 
6603         * @type Number */
6604         END : 35,
6605         /** Key constant 
6606         * @type Number */
6607         HOME : 36,
6608         /** Key constant 
6609         * @type Number */
6610         LEFT : 37,
6611         /** Key constant 
6612         * @type Number */
6613         UP : 38,
6614         /** Key constant 
6615         * @type Number */
6616         RIGHT : 39,
6617         /** Key constant 
6618         * @type Number */
6619         DOWN : 40,
6620         /** Key constant 
6621         * @type Number */
6622         DELETE : 46,
6623         /** Key constant 
6624         * @type Number */
6625         F5 : 116,
6626
6627            /** @private */
6628         setEvent : function(e){
6629             if(e == this || (e && e.browserEvent)){ // already wrapped
6630                 return e;
6631             }
6632             this.browserEvent = e;
6633             if(e){
6634                 // normalize buttons
6635                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6636                 if(e.type == 'click' && this.button == -1){
6637                     this.button = 0;
6638                 }
6639                 this.type = e.type;
6640                 this.shiftKey = e.shiftKey;
6641                 // mac metaKey behaves like ctrlKey
6642                 this.ctrlKey = e.ctrlKey || e.metaKey;
6643                 this.altKey = e.altKey;
6644                 // in getKey these will be normalized for the mac
6645                 this.keyCode = e.keyCode;
6646                 // keyup warnings on firefox.
6647                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6648                 // cache the target for the delayed and or buffered events
6649                 this.target = E.getTarget(e);
6650                 // same for XY
6651                 this.xy = E.getXY(e);
6652             }else{
6653                 this.button = -1;
6654                 this.shiftKey = false;
6655                 this.ctrlKey = false;
6656                 this.altKey = false;
6657                 this.keyCode = 0;
6658                 this.charCode =0;
6659                 this.target = null;
6660                 this.xy = [0, 0];
6661             }
6662             return this;
6663         },
6664
6665         /**
6666          * Stop the event (preventDefault and stopPropagation)
6667          */
6668         stopEvent : function(){
6669             if(this.browserEvent){
6670                 if(this.browserEvent.type == 'mousedown'){
6671                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6672                 }
6673                 E.stopEvent(this.browserEvent);
6674             }
6675         },
6676
6677         /**
6678          * Prevents the browsers default handling of the event.
6679          */
6680         preventDefault : function(){
6681             if(this.browserEvent){
6682                 E.preventDefault(this.browserEvent);
6683             }
6684         },
6685
6686         /** @private */
6687         isNavKeyPress : function(){
6688             var k = this.keyCode;
6689             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6690             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6691         },
6692
6693         isSpecialKey : function(){
6694             var k = this.keyCode;
6695             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6696             (k == 16) || (k == 17) ||
6697             (k >= 18 && k <= 20) ||
6698             (k >= 33 && k <= 35) ||
6699             (k >= 36 && k <= 39) ||
6700             (k >= 44 && k <= 45);
6701         },
6702         /**
6703          * Cancels bubbling of the event.
6704          */
6705         stopPropagation : function(){
6706             if(this.browserEvent){
6707                 if(this.type == 'mousedown'){
6708                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6709                 }
6710                 E.stopPropagation(this.browserEvent);
6711             }
6712         },
6713
6714         /**
6715          * Gets the key code for the event.
6716          * @return {Number}
6717          */
6718         getCharCode : function(){
6719             return this.charCode || this.keyCode;
6720         },
6721
6722         /**
6723          * Returns a normalized keyCode for the event.
6724          * @return {Number} The key code
6725          */
6726         getKey : function(){
6727             var k = this.keyCode || this.charCode;
6728             return Roo.isSafari ? (safariKeys[k] || k) : k;
6729         },
6730
6731         /**
6732          * Gets the x coordinate of the event.
6733          * @return {Number}
6734          */
6735         getPageX : function(){
6736             return this.xy[0];
6737         },
6738
6739         /**
6740          * Gets the y coordinate of the event.
6741          * @return {Number}
6742          */
6743         getPageY : function(){
6744             return this.xy[1];
6745         },
6746
6747         /**
6748          * Gets the time of the event.
6749          * @return {Number}
6750          */
6751         getTime : function(){
6752             if(this.browserEvent){
6753                 return E.getTime(this.browserEvent);
6754             }
6755             return null;
6756         },
6757
6758         /**
6759          * Gets the page coordinates of the event.
6760          * @return {Array} The xy values like [x, y]
6761          */
6762         getXY : function(){
6763             return this.xy;
6764         },
6765
6766         /**
6767          * Gets the target for the event.
6768          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6769          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6770                 search as a number or element (defaults to 10 || document.body)
6771          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6772          * @return {HTMLelement}
6773          */
6774         getTarget : function(selector, maxDepth, returnEl){
6775             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6776         },
6777         /**
6778          * Gets the related target.
6779          * @return {HTMLElement}
6780          */
6781         getRelatedTarget : function(){
6782             if(this.browserEvent){
6783                 return E.getRelatedTarget(this.browserEvent);
6784             }
6785             return null;
6786         },
6787
6788         /**
6789          * Normalizes mouse wheel delta across browsers
6790          * @return {Number} The delta
6791          */
6792         getWheelDelta : function(){
6793             var e = this.browserEvent;
6794             var delta = 0;
6795             if(e.wheelDelta){ /* IE/Opera. */
6796                 delta = e.wheelDelta/120;
6797             }else if(e.detail){ /* Mozilla case. */
6798                 delta = -e.detail/3;
6799             }
6800             return delta;
6801         },
6802
6803         /**
6804          * Returns true if the control, meta, shift or alt key was pressed during this event.
6805          * @return {Boolean}
6806          */
6807         hasModifier : function(){
6808             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6809         },
6810
6811         /**
6812          * Returns true if the target of this event equals el or is a child of el
6813          * @param {String/HTMLElement/Element} el
6814          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6815          * @return {Boolean}
6816          */
6817         within : function(el, related){
6818             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6819             return t && Roo.fly(el).contains(t);
6820         },
6821
6822         getPoint : function(){
6823             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6824         }
6825     };
6826
6827     return new Roo.EventObjectImpl();
6828 }();
6829             
6830     /*
6831  * Based on:
6832  * Ext JS Library 1.1.1
6833  * Copyright(c) 2006-2007, Ext JS, LLC.
6834  *
6835  * Originally Released Under LGPL - original licence link has changed is not relivant.
6836  *
6837  * Fork - LGPL
6838  * <script type="text/javascript">
6839  */
6840
6841  
6842 // was in Composite Element!??!?!
6843  
6844 (function(){
6845     var D = Roo.lib.Dom;
6846     var E = Roo.lib.Event;
6847     var A = Roo.lib.Anim;
6848
6849     // local style camelizing for speed
6850     var propCache = {};
6851     var camelRe = /(-[a-z])/gi;
6852     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6853     var view = document.defaultView;
6854
6855 /**
6856  * @class Roo.Element
6857  * Represents an Element in the DOM.<br><br>
6858  * Usage:<br>
6859 <pre><code>
6860 var el = Roo.get("my-div");
6861
6862 // or with getEl
6863 var el = getEl("my-div");
6864
6865 // or with a DOM element
6866 var el = Roo.get(myDivElement);
6867 </code></pre>
6868  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6869  * each call instead of constructing a new one.<br><br>
6870  * <b>Animations</b><br />
6871  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6872  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6873 <pre>
6874 Option    Default   Description
6875 --------- --------  ---------------------------------------------
6876 duration  .35       The duration of the animation in seconds
6877 easing    easeOut   The YUI easing method
6878 callback  none      A function to execute when the anim completes
6879 scope     this      The scope (this) of the callback function
6880 </pre>
6881 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6882 * manipulate the animation. Here's an example:
6883 <pre><code>
6884 var el = Roo.get("my-div");
6885
6886 // no animation
6887 el.setWidth(100);
6888
6889 // default animation
6890 el.setWidth(100, true);
6891
6892 // animation with some options set
6893 el.setWidth(100, {
6894     duration: 1,
6895     callback: this.foo,
6896     scope: this
6897 });
6898
6899 // using the "anim" property to get the Anim object
6900 var opt = {
6901     duration: 1,
6902     callback: this.foo,
6903     scope: this
6904 };
6905 el.setWidth(100, opt);
6906 ...
6907 if(opt.anim.isAnimated()){
6908     opt.anim.stop();
6909 }
6910 </code></pre>
6911 * <b> Composite (Collections of) Elements</b><br />
6912  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6913  * @constructor Create a new Element directly.
6914  * @param {String/HTMLElement} element
6915  * @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).
6916  */
6917     Roo.Element = function(element, forceNew){
6918         var dom = typeof element == "string" ?
6919                 document.getElementById(element) : element;
6920         if(!dom){ // invalid id/element
6921             return null;
6922         }
6923         var id = dom.id;
6924         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6925             return Roo.Element.cache[id];
6926         }
6927
6928         /**
6929          * The DOM element
6930          * @type HTMLElement
6931          */
6932         this.dom = dom;
6933
6934         /**
6935          * The DOM element ID
6936          * @type String
6937          */
6938         this.id = id || Roo.id(dom);
6939     };
6940
6941     var El = Roo.Element;
6942
6943     El.prototype = {
6944         /**
6945          * The element's default display mode  (defaults to "")
6946          * @type String
6947          */
6948         originalDisplay : "",
6949
6950         visibilityMode : 1,
6951         /**
6952          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6953          * @type String
6954          */
6955         defaultUnit : "px",
6956         /**
6957          * Sets the element's visibility mode. When setVisible() is called it
6958          * will use this to determine whether to set the visibility or the display property.
6959          * @param visMode Element.VISIBILITY or Element.DISPLAY
6960          * @return {Roo.Element} this
6961          */
6962         setVisibilityMode : function(visMode){
6963             this.visibilityMode = visMode;
6964             return this;
6965         },
6966         /**
6967          * Convenience method for setVisibilityMode(Element.DISPLAY)
6968          * @param {String} display (optional) What to set display to when visible
6969          * @return {Roo.Element} this
6970          */
6971         enableDisplayMode : function(display){
6972             this.setVisibilityMode(El.DISPLAY);
6973             if(typeof display != "undefined") this.originalDisplay = display;
6974             return this;
6975         },
6976
6977         /**
6978          * 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)
6979          * @param {String} selector The simple selector to test
6980          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6981                 search as a number or element (defaults to 10 || document.body)
6982          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6983          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6984          */
6985         findParent : function(simpleSelector, maxDepth, returnEl){
6986             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6987             maxDepth = maxDepth || 50;
6988             if(typeof maxDepth != "number"){
6989                 stopEl = Roo.getDom(maxDepth);
6990                 maxDepth = 10;
6991             }
6992             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6993                 if(dq.is(p, simpleSelector)){
6994                     return returnEl ? Roo.get(p) : p;
6995                 }
6996                 depth++;
6997                 p = p.parentNode;
6998             }
6999             return null;
7000         },
7001
7002
7003         /**
7004          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7005          * @param {String} selector The simple selector to test
7006          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7007                 search as a number or element (defaults to 10 || document.body)
7008          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7009          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7010          */
7011         findParentNode : function(simpleSelector, maxDepth, returnEl){
7012             var p = Roo.fly(this.dom.parentNode, '_internal');
7013             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7014         },
7015
7016         /**
7017          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7018          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7019          * @param {String} selector The simple selector to test
7020          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7021                 search as a number or element (defaults to 10 || document.body)
7022          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7023          */
7024         up : function(simpleSelector, maxDepth){
7025             return this.findParentNode(simpleSelector, maxDepth, true);
7026         },
7027
7028
7029
7030         /**
7031          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7032          * @param {String} selector The simple selector to test
7033          * @return {Boolean} True if this element matches the selector, else false
7034          */
7035         is : function(simpleSelector){
7036             return Roo.DomQuery.is(this.dom, simpleSelector);
7037         },
7038
7039         /**
7040          * Perform animation on this element.
7041          * @param {Object} args The YUI animation control args
7042          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7043          * @param {Function} onComplete (optional) Function to call when animation completes
7044          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7045          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7046          * @return {Roo.Element} this
7047          */
7048         animate : function(args, duration, onComplete, easing, animType){
7049             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7050             return this;
7051         },
7052
7053         /*
7054          * @private Internal animation call
7055          */
7056         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7057             animType = animType || 'run';
7058             opt = opt || {};
7059             var anim = Roo.lib.Anim[animType](
7060                 this.dom, args,
7061                 (opt.duration || defaultDur) || .35,
7062                 (opt.easing || defaultEase) || 'easeOut',
7063                 function(){
7064                     Roo.callback(cb, this);
7065                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7066                 },
7067                 this
7068             );
7069             opt.anim = anim;
7070             return anim;
7071         },
7072
7073         // private legacy anim prep
7074         preanim : function(a, i){
7075             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7076         },
7077
7078         /**
7079          * Removes worthless text nodes
7080          * @param {Boolean} forceReclean (optional) By default the element
7081          * keeps track if it has been cleaned already so
7082          * you can call this over and over. However, if you update the element and
7083          * need to force a reclean, you can pass true.
7084          */
7085         clean : function(forceReclean){
7086             if(this.isCleaned && forceReclean !== true){
7087                 return this;
7088             }
7089             var ns = /\S/;
7090             var d = this.dom, n = d.firstChild, ni = -1;
7091             while(n){
7092                 var nx = n.nextSibling;
7093                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7094                     d.removeChild(n);
7095                 }else{
7096                     n.nodeIndex = ++ni;
7097                 }
7098                 n = nx;
7099             }
7100             this.isCleaned = true;
7101             return this;
7102         },
7103
7104         // private
7105         calcOffsetsTo : function(el){
7106             el = Roo.get(el);
7107             var d = el.dom;
7108             var restorePos = false;
7109             if(el.getStyle('position') == 'static'){
7110                 el.position('relative');
7111                 restorePos = true;
7112             }
7113             var x = 0, y =0;
7114             var op = this.dom;
7115             while(op && op != d && op.tagName != 'HTML'){
7116                 x+= op.offsetLeft;
7117                 y+= op.offsetTop;
7118                 op = op.offsetParent;
7119             }
7120             if(restorePos){
7121                 el.position('static');
7122             }
7123             return [x, y];
7124         },
7125
7126         /**
7127          * Scrolls this element into view within the passed container.
7128          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7129          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7130          * @return {Roo.Element} this
7131          */
7132         scrollIntoView : function(container, hscroll){
7133             var c = Roo.getDom(container) || document.body;
7134             var el = this.dom;
7135
7136             var o = this.calcOffsetsTo(c),
7137                 l = o[0],
7138                 t = o[1],
7139                 b = t+el.offsetHeight,
7140                 r = l+el.offsetWidth;
7141
7142             var ch = c.clientHeight;
7143             var ct = parseInt(c.scrollTop, 10);
7144             var cl = parseInt(c.scrollLeft, 10);
7145             var cb = ct + ch;
7146             var cr = cl + c.clientWidth;
7147
7148             if(t < ct){
7149                 c.scrollTop = t;
7150             }else if(b > cb){
7151                 c.scrollTop = b-ch;
7152             }
7153
7154             if(hscroll !== false){
7155                 if(l < cl){
7156                     c.scrollLeft = l;
7157                 }else if(r > cr){
7158                     c.scrollLeft = r-c.clientWidth;
7159                 }
7160             }
7161             return this;
7162         },
7163
7164         // private
7165         scrollChildIntoView : function(child, hscroll){
7166             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7167         },
7168
7169         /**
7170          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7171          * the new height may not be available immediately.
7172          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7173          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7174          * @param {Function} onComplete (optional) Function to call when animation completes
7175          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7176          * @return {Roo.Element} this
7177          */
7178         autoHeight : function(animate, duration, onComplete, easing){
7179             var oldHeight = this.getHeight();
7180             this.clip();
7181             this.setHeight(1); // force clipping
7182             setTimeout(function(){
7183                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7184                 if(!animate){
7185                     this.setHeight(height);
7186                     this.unclip();
7187                     if(typeof onComplete == "function"){
7188                         onComplete();
7189                     }
7190                 }else{
7191                     this.setHeight(oldHeight); // restore original height
7192                     this.setHeight(height, animate, duration, function(){
7193                         this.unclip();
7194                         if(typeof onComplete == "function") onComplete();
7195                     }.createDelegate(this), easing);
7196                 }
7197             }.createDelegate(this), 0);
7198             return this;
7199         },
7200
7201         /**
7202          * Returns true if this element is an ancestor of the passed element
7203          * @param {HTMLElement/String} el The element to check
7204          * @return {Boolean} True if this element is an ancestor of el, else false
7205          */
7206         contains : function(el){
7207             if(!el){return false;}
7208             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7209         },
7210
7211         /**
7212          * Checks whether the element is currently visible using both visibility and display properties.
7213          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7214          * @return {Boolean} True if the element is currently visible, else false
7215          */
7216         isVisible : function(deep) {
7217             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7218             if(deep !== true || !vis){
7219                 return vis;
7220             }
7221             var p = this.dom.parentNode;
7222             while(p && p.tagName.toLowerCase() != "body"){
7223                 if(!Roo.fly(p, '_isVisible').isVisible()){
7224                     return false;
7225                 }
7226                 p = p.parentNode;
7227             }
7228             return true;
7229         },
7230
7231         /**
7232          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7233          * @param {String} selector The CSS selector
7234          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7235          * @return {CompositeElement/CompositeElementLite} The composite element
7236          */
7237         select : function(selector, unique){
7238             return El.select(selector, unique, this.dom);
7239         },
7240
7241         /**
7242          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7243          * @param {String} selector The CSS selector
7244          * @return {Array} An array of the matched nodes
7245          */
7246         query : function(selector, unique){
7247             return Roo.DomQuery.select(selector, this.dom);
7248         },
7249
7250         /**
7251          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7252          * @param {String} selector The CSS selector
7253          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7254          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7255          */
7256         child : function(selector, returnDom){
7257             var n = Roo.DomQuery.selectNode(selector, this.dom);
7258             return returnDom ? n : Roo.get(n);
7259         },
7260
7261         /**
7262          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7263          * @param {String} selector The CSS selector
7264          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7265          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7266          */
7267         down : function(selector, returnDom){
7268             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7269             return returnDom ? n : Roo.get(n);
7270         },
7271
7272         /**
7273          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7274          * @param {String} group The group the DD object is member of
7275          * @param {Object} config The DD config object
7276          * @param {Object} overrides An object containing methods to override/implement on the DD object
7277          * @return {Roo.dd.DD} The DD object
7278          */
7279         initDD : function(group, config, overrides){
7280             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7281             return Roo.apply(dd, overrides);
7282         },
7283
7284         /**
7285          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7286          * @param {String} group The group the DDProxy object is member of
7287          * @param {Object} config The DDProxy config object
7288          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7289          * @return {Roo.dd.DDProxy} The DDProxy object
7290          */
7291         initDDProxy : function(group, config, overrides){
7292             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7293             return Roo.apply(dd, overrides);
7294         },
7295
7296         /**
7297          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7298          * @param {String} group The group the DDTarget object is member of
7299          * @param {Object} config The DDTarget config object
7300          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7301          * @return {Roo.dd.DDTarget} The DDTarget object
7302          */
7303         initDDTarget : function(group, config, overrides){
7304             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7305             return Roo.apply(dd, overrides);
7306         },
7307
7308         /**
7309          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7310          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7311          * @param {Boolean} visible Whether the element is visible
7312          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7313          * @return {Roo.Element} this
7314          */
7315          setVisible : function(visible, animate){
7316             if(!animate || !A){
7317                 if(this.visibilityMode == El.DISPLAY){
7318                     this.setDisplayed(visible);
7319                 }else{
7320                     this.fixDisplay();
7321                     this.dom.style.visibility = visible ? "visible" : "hidden";
7322                 }
7323             }else{
7324                 // closure for composites
7325                 var dom = this.dom;
7326                 var visMode = this.visibilityMode;
7327                 if(visible){
7328                     this.setOpacity(.01);
7329                     this.setVisible(true);
7330                 }
7331                 this.anim({opacity: { to: (visible?1:0) }},
7332                       this.preanim(arguments, 1),
7333                       null, .35, 'easeIn', function(){
7334                          if(!visible){
7335                              if(visMode == El.DISPLAY){
7336                                  dom.style.display = "none";
7337                              }else{
7338                                  dom.style.visibility = "hidden";
7339                              }
7340                              Roo.get(dom).setOpacity(1);
7341                          }
7342                      });
7343             }
7344             return this;
7345         },
7346
7347         /**
7348          * Returns true if display is not "none"
7349          * @return {Boolean}
7350          */
7351         isDisplayed : function() {
7352             return this.getStyle("display") != "none";
7353         },
7354
7355         /**
7356          * Toggles the element's visibility or display, depending on visibility mode.
7357          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7358          * @return {Roo.Element} this
7359          */
7360         toggle : function(animate){
7361             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7362             return this;
7363         },
7364
7365         /**
7366          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7367          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7368          * @return {Roo.Element} this
7369          */
7370         setDisplayed : function(value) {
7371             if(typeof value == "boolean"){
7372                value = value ? this.originalDisplay : "none";
7373             }
7374             this.setStyle("display", value);
7375             return this;
7376         },
7377
7378         /**
7379          * Tries to focus the element. Any exceptions are caught and ignored.
7380          * @return {Roo.Element} this
7381          */
7382         focus : function() {
7383             try{
7384                 this.dom.focus();
7385             }catch(e){}
7386             return this;
7387         },
7388
7389         /**
7390          * Tries to blur the element. Any exceptions are caught and ignored.
7391          * @return {Roo.Element} this
7392          */
7393         blur : function() {
7394             try{
7395                 this.dom.blur();
7396             }catch(e){}
7397             return this;
7398         },
7399
7400         /**
7401          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7402          * @param {String/Array} className The CSS class to add, or an array of classes
7403          * @return {Roo.Element} this
7404          */
7405         addClass : function(className){
7406             if(className instanceof Array){
7407                 for(var i = 0, len = className.length; i < len; i++) {
7408                     this.addClass(className[i]);
7409                 }
7410             }else{
7411                 if(className && !this.hasClass(className)){
7412                     this.dom.className = this.dom.className + " " + className;
7413                 }
7414             }
7415             return this;
7416         },
7417
7418         /**
7419          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7420          * @param {String/Array} className The CSS class to add, or an array of classes
7421          * @return {Roo.Element} this
7422          */
7423         radioClass : function(className){
7424             var siblings = this.dom.parentNode.childNodes;
7425             for(var i = 0; i < siblings.length; i++) {
7426                 var s = siblings[i];
7427                 if(s.nodeType == 1){
7428                     Roo.get(s).removeClass(className);
7429                 }
7430             }
7431             this.addClass(className);
7432             return this;
7433         },
7434
7435         /**
7436          * Removes one or more CSS classes from the element.
7437          * @param {String/Array} className The CSS class to remove, or an array of classes
7438          * @return {Roo.Element} this
7439          */
7440         removeClass : function(className){
7441             if(!className || !this.dom.className){
7442                 return this;
7443             }
7444             if(className instanceof Array){
7445                 for(var i = 0, len = className.length; i < len; i++) {
7446                     this.removeClass(className[i]);
7447                 }
7448             }else{
7449                 if(this.hasClass(className)){
7450                     var re = this.classReCache[className];
7451                     if (!re) {
7452                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7453                        this.classReCache[className] = re;
7454                     }
7455                     this.dom.className =
7456                         this.dom.className.replace(re, " ");
7457                 }
7458             }
7459             return this;
7460         },
7461
7462         // private
7463         classReCache: {},
7464
7465         /**
7466          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7467          * @param {String} className The CSS class to toggle
7468          * @return {Roo.Element} this
7469          */
7470         toggleClass : function(className){
7471             if(this.hasClass(className)){
7472                 this.removeClass(className);
7473             }else{
7474                 this.addClass(className);
7475             }
7476             return this;
7477         },
7478
7479         /**
7480          * Checks if the specified CSS class exists on this element's DOM node.
7481          * @param {String} className The CSS class to check for
7482          * @return {Boolean} True if the class exists, else false
7483          */
7484         hasClass : function(className){
7485             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7486         },
7487
7488         /**
7489          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7490          * @param {String} oldClassName The CSS class to replace
7491          * @param {String} newClassName The replacement CSS class
7492          * @return {Roo.Element} this
7493          */
7494         replaceClass : function(oldClassName, newClassName){
7495             this.removeClass(oldClassName);
7496             this.addClass(newClassName);
7497             return this;
7498         },
7499
7500         /**
7501          * Returns an object with properties matching the styles requested.
7502          * For example, el.getStyles('color', 'font-size', 'width') might return
7503          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7504          * @param {String} style1 A style name
7505          * @param {String} style2 A style name
7506          * @param {String} etc.
7507          * @return {Object} The style object
7508          */
7509         getStyles : function(){
7510             var a = arguments, len = a.length, r = {};
7511             for(var i = 0; i < len; i++){
7512                 r[a[i]] = this.getStyle(a[i]);
7513             }
7514             return r;
7515         },
7516
7517         /**
7518          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7519          * @param {String} property The style property whose value is returned.
7520          * @return {String} The current value of the style property for this element.
7521          */
7522         getStyle : function(){
7523             return view && view.getComputedStyle ?
7524                 function(prop){
7525                     var el = this.dom, v, cs, camel;
7526                     if(prop == 'float'){
7527                         prop = "cssFloat";
7528                     }
7529                     if(el.style && (v = el.style[prop])){
7530                         return v;
7531                     }
7532                     if(cs = view.getComputedStyle(el, "")){
7533                         if(!(camel = propCache[prop])){
7534                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7535                         }
7536                         return cs[camel];
7537                     }
7538                     return null;
7539                 } :
7540                 function(prop){
7541                     var el = this.dom, v, cs, camel;
7542                     if(prop == 'opacity'){
7543                         if(typeof el.style.filter == 'string'){
7544                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7545                             if(m){
7546                                 var fv = parseFloat(m[1]);
7547                                 if(!isNaN(fv)){
7548                                     return fv ? fv / 100 : 0;
7549                                 }
7550                             }
7551                         }
7552                         return 1;
7553                     }else if(prop == 'float'){
7554                         prop = "styleFloat";
7555                     }
7556                     if(!(camel = propCache[prop])){
7557                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7558                     }
7559                     if(v = el.style[camel]){
7560                         return v;
7561                     }
7562                     if(cs = el.currentStyle){
7563                         return cs[camel];
7564                     }
7565                     return null;
7566                 };
7567         }(),
7568
7569         /**
7570          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7571          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7572          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7573          * @return {Roo.Element} this
7574          */
7575         setStyle : function(prop, value){
7576             if(typeof prop == "string"){
7577                 
7578                 if (prop == 'float') {
7579                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7580                     return this;
7581                 }
7582                 
7583                 var camel;
7584                 if(!(camel = propCache[prop])){
7585                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                 }
7587                 
7588                 if(camel == 'opacity') {
7589                     this.setOpacity(value);
7590                 }else{
7591                     this.dom.style[camel] = value;
7592                 }
7593             }else{
7594                 for(var style in prop){
7595                     if(typeof prop[style] != "function"){
7596                        this.setStyle(style, prop[style]);
7597                     }
7598                 }
7599             }
7600             return this;
7601         },
7602
7603         /**
7604          * More flexible version of {@link #setStyle} for setting style properties.
7605          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7606          * a function which returns such a specification.
7607          * @return {Roo.Element} this
7608          */
7609         applyStyles : function(style){
7610             Roo.DomHelper.applyStyles(this.dom, style);
7611             return this;
7612         },
7613
7614         /**
7615           * 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).
7616           * @return {Number} The X position of the element
7617           */
7618         getX : function(){
7619             return D.getX(this.dom);
7620         },
7621
7622         /**
7623           * 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).
7624           * @return {Number} The Y position of the element
7625           */
7626         getY : function(){
7627             return D.getY(this.dom);
7628         },
7629
7630         /**
7631           * 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).
7632           * @return {Array} The XY position of the element
7633           */
7634         getXY : function(){
7635             return D.getXY(this.dom);
7636         },
7637
7638         /**
7639          * 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).
7640          * @param {Number} The X position of the element
7641          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7642          * @return {Roo.Element} this
7643          */
7644         setX : function(x, animate){
7645             if(!animate || !A){
7646                 D.setX(this.dom, x);
7647             }else{
7648                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7649             }
7650             return this;
7651         },
7652
7653         /**
7654          * 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).
7655          * @param {Number} The Y position of the element
7656          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7657          * @return {Roo.Element} this
7658          */
7659         setY : function(y, animate){
7660             if(!animate || !A){
7661                 D.setY(this.dom, y);
7662             }else{
7663                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7664             }
7665             return this;
7666         },
7667
7668         /**
7669          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7670          * @param {String} left The left CSS property value
7671          * @return {Roo.Element} this
7672          */
7673         setLeft : function(left){
7674             this.setStyle("left", this.addUnits(left));
7675             return this;
7676         },
7677
7678         /**
7679          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7680          * @param {String} top The top CSS property value
7681          * @return {Roo.Element} this
7682          */
7683         setTop : function(top){
7684             this.setStyle("top", this.addUnits(top));
7685             return this;
7686         },
7687
7688         /**
7689          * Sets the element's CSS right style.
7690          * @param {String} right The right CSS property value
7691          * @return {Roo.Element} this
7692          */
7693         setRight : function(right){
7694             this.setStyle("right", this.addUnits(right));
7695             return this;
7696         },
7697
7698         /**
7699          * Sets the element's CSS bottom style.
7700          * @param {String} bottom The bottom CSS property value
7701          * @return {Roo.Element} this
7702          */
7703         setBottom : function(bottom){
7704             this.setStyle("bottom", this.addUnits(bottom));
7705             return this;
7706         },
7707
7708         /**
7709          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7710          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7711          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7712          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7713          * @return {Roo.Element} this
7714          */
7715         setXY : function(pos, animate){
7716             if(!animate || !A){
7717                 D.setXY(this.dom, pos);
7718             }else{
7719                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7720             }
7721             return this;
7722         },
7723
7724         /**
7725          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7726          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7727          * @param {Number} x X value for new position (coordinates are page-based)
7728          * @param {Number} y Y value for new position (coordinates are page-based)
7729          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7730          * @return {Roo.Element} this
7731          */
7732         setLocation : function(x, y, animate){
7733             this.setXY([x, y], this.preanim(arguments, 2));
7734             return this;
7735         },
7736
7737         /**
7738          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7739          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7740          * @param {Number} x X value for new position (coordinates are page-based)
7741          * @param {Number} y Y value for new position (coordinates are page-based)
7742          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7743          * @return {Roo.Element} this
7744          */
7745         moveTo : function(x, y, animate){
7746             this.setXY([x, y], this.preanim(arguments, 2));
7747             return this;
7748         },
7749
7750         /**
7751          * Returns the region of the given element.
7752          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7753          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7754          */
7755         getRegion : function(){
7756             return D.getRegion(this.dom);
7757         },
7758
7759         /**
7760          * Returns the offset height of the element
7761          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7762          * @return {Number} The element's height
7763          */
7764         getHeight : function(contentHeight){
7765             var h = this.dom.offsetHeight || 0;
7766             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7767         },
7768
7769         /**
7770          * Returns the offset width of the element
7771          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7772          * @return {Number} The element's width
7773          */
7774         getWidth : function(contentWidth){
7775             var w = this.dom.offsetWidth || 0;
7776             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7777         },
7778
7779         /**
7780          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7781          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7782          * if a height has not been set using CSS.
7783          * @return {Number}
7784          */
7785         getComputedHeight : function(){
7786             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7787             if(!h){
7788                 h = parseInt(this.getStyle('height'), 10) || 0;
7789                 if(!this.isBorderBox()){
7790                     h += this.getFrameWidth('tb');
7791                 }
7792             }
7793             return h;
7794         },
7795
7796         /**
7797          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7798          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7799          * if a width has not been set using CSS.
7800          * @return {Number}
7801          */
7802         getComputedWidth : function(){
7803             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7804             if(!w){
7805                 w = parseInt(this.getStyle('width'), 10) || 0;
7806                 if(!this.isBorderBox()){
7807                     w += this.getFrameWidth('lr');
7808                 }
7809             }
7810             return w;
7811         },
7812
7813         /**
7814          * Returns the size of the element.
7815          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7816          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7817          */
7818         getSize : function(contentSize){
7819             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7820         },
7821
7822         /**
7823          * Returns the width and height of the viewport.
7824          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7825          */
7826         getViewSize : function(){
7827             var d = this.dom, doc = document, aw = 0, ah = 0;
7828             if(d == doc || d == doc.body){
7829                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7830             }else{
7831                 return {
7832                     width : d.clientWidth,
7833                     height: d.clientHeight
7834                 };
7835             }
7836         },
7837
7838         /**
7839          * Returns the value of the "value" attribute
7840          * @param {Boolean} asNumber true to parse the value as a number
7841          * @return {String/Number}
7842          */
7843         getValue : function(asNumber){
7844             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7845         },
7846
7847         // private
7848         adjustWidth : function(width){
7849             if(typeof width == "number"){
7850                 if(this.autoBoxAdjust && !this.isBorderBox()){
7851                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7852                 }
7853                 if(width < 0){
7854                     width = 0;
7855                 }
7856             }
7857             return width;
7858         },
7859
7860         // private
7861         adjustHeight : function(height){
7862             if(typeof height == "number"){
7863                if(this.autoBoxAdjust && !this.isBorderBox()){
7864                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7865                }
7866                if(height < 0){
7867                    height = 0;
7868                }
7869             }
7870             return height;
7871         },
7872
7873         /**
7874          * Set the width of the element
7875          * @param {Number} width The new width
7876          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7877          * @return {Roo.Element} this
7878          */
7879         setWidth : function(width, animate){
7880             width = this.adjustWidth(width);
7881             if(!animate || !A){
7882                 this.dom.style.width = this.addUnits(width);
7883             }else{
7884                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7885             }
7886             return this;
7887         },
7888
7889         /**
7890          * Set the height of the element
7891          * @param {Number} height The new height
7892          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7893          * @return {Roo.Element} this
7894          */
7895          setHeight : function(height, animate){
7896             height = this.adjustHeight(height);
7897             if(!animate || !A){
7898                 this.dom.style.height = this.addUnits(height);
7899             }else{
7900                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7901             }
7902             return this;
7903         },
7904
7905         /**
7906          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7907          * @param {Number} width The new width
7908          * @param {Number} height The new height
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          setSize : function(width, height, animate){
7913             if(typeof width == "object"){ // in case of object from getSize()
7914                 height = width.height; width = width.width;
7915             }
7916             width = this.adjustWidth(width); height = this.adjustHeight(height);
7917             if(!animate || !A){
7918                 this.dom.style.width = this.addUnits(width);
7919                 this.dom.style.height = this.addUnits(height);
7920             }else{
7921                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7922             }
7923             return this;
7924         },
7925
7926         /**
7927          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7928          * @param {Number} x X value for new position (coordinates are page-based)
7929          * @param {Number} y Y value for new position (coordinates are page-based)
7930          * @param {Number} width The new width
7931          * @param {Number} height The new height
7932          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7933          * @return {Roo.Element} this
7934          */
7935         setBounds : function(x, y, width, height, animate){
7936             if(!animate || !A){
7937                 this.setSize(width, height);
7938                 this.setLocation(x, y);
7939             }else{
7940                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7941                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7942                               this.preanim(arguments, 4), 'motion');
7943             }
7944             return this;
7945         },
7946
7947         /**
7948          * 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.
7949          * @param {Roo.lib.Region} region The region to fill
7950          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7951          * @return {Roo.Element} this
7952          */
7953         setRegion : function(region, animate){
7954             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7955             return this;
7956         },
7957
7958         /**
7959          * Appends an event handler
7960          *
7961          * @param {String}   eventName     The type of event to append
7962          * @param {Function} fn        The method the event invokes
7963          * @param {Object} scope       (optional) The scope (this object) of the fn
7964          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7965          */
7966         addListener : function(eventName, fn, scope, options){
7967             if (this.dom) {
7968                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7969             }
7970         },
7971
7972         /**
7973          * Removes an event handler from this element
7974          * @param {String} eventName the type of event to remove
7975          * @param {Function} fn the method the event invokes
7976          * @return {Roo.Element} this
7977          */
7978         removeListener : function(eventName, fn){
7979             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7980             return this;
7981         },
7982
7983         /**
7984          * Removes all previous added listeners from this element
7985          * @return {Roo.Element} this
7986          */
7987         removeAllListeners : function(){
7988             E.purgeElement(this.dom);
7989             return this;
7990         },
7991
7992         relayEvent : function(eventName, observable){
7993             this.on(eventName, function(e){
7994                 observable.fireEvent(eventName, e);
7995             });
7996         },
7997
7998         /**
7999          * Set the opacity of the element
8000          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8001          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8002          * @return {Roo.Element} this
8003          */
8004          setOpacity : function(opacity, animate){
8005             if(!animate || !A){
8006                 var s = this.dom.style;
8007                 if(Roo.isIE){
8008                     s.zoom = 1;
8009                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8010                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8011                 }else{
8012                     s.opacity = opacity;
8013                 }
8014             }else{
8015                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8016             }
8017             return this;
8018         },
8019
8020         /**
8021          * Gets the left X coordinate
8022          * @param {Boolean} local True to get the local css position instead of page coordinate
8023          * @return {Number}
8024          */
8025         getLeft : function(local){
8026             if(!local){
8027                 return this.getX();
8028             }else{
8029                 return parseInt(this.getStyle("left"), 10) || 0;
8030             }
8031         },
8032
8033         /**
8034          * Gets the right X coordinate of the element (element X position + element width)
8035          * @param {Boolean} local True to get the local css position instead of page coordinate
8036          * @return {Number}
8037          */
8038         getRight : function(local){
8039             if(!local){
8040                 return this.getX() + this.getWidth();
8041             }else{
8042                 return (this.getLeft(true) + this.getWidth()) || 0;
8043             }
8044         },
8045
8046         /**
8047          * Gets the top Y coordinate
8048          * @param {Boolean} local True to get the local css position instead of page coordinate
8049          * @return {Number}
8050          */
8051         getTop : function(local) {
8052             if(!local){
8053                 return this.getY();
8054             }else{
8055                 return parseInt(this.getStyle("top"), 10) || 0;
8056             }
8057         },
8058
8059         /**
8060          * Gets the bottom Y coordinate of the element (element Y position + element height)
8061          * @param {Boolean} local True to get the local css position instead of page coordinate
8062          * @return {Number}
8063          */
8064         getBottom : function(local){
8065             if(!local){
8066                 return this.getY() + this.getHeight();
8067             }else{
8068                 return (this.getTop(true) + this.getHeight()) || 0;
8069             }
8070         },
8071
8072         /**
8073         * Initializes positioning on this element. If a desired position is not passed, it will make the
8074         * the element positioned relative IF it is not already positioned.
8075         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8076         * @param {Number} zIndex (optional) The zIndex to apply
8077         * @param {Number} x (optional) Set the page X position
8078         * @param {Number} y (optional) Set the page Y position
8079         */
8080         position : function(pos, zIndex, x, y){
8081             if(!pos){
8082                if(this.getStyle('position') == 'static'){
8083                    this.setStyle('position', 'relative');
8084                }
8085             }else{
8086                 this.setStyle("position", pos);
8087             }
8088             if(zIndex){
8089                 this.setStyle("z-index", zIndex);
8090             }
8091             if(x !== undefined && y !== undefined){
8092                 this.setXY([x, y]);
8093             }else if(x !== undefined){
8094                 this.setX(x);
8095             }else if(y !== undefined){
8096                 this.setY(y);
8097             }
8098         },
8099
8100         /**
8101         * Clear positioning back to the default when the document was loaded
8102         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8103         * @return {Roo.Element} this
8104          */
8105         clearPositioning : function(value){
8106             value = value ||'';
8107             this.setStyle({
8108                 "left": value,
8109                 "right": value,
8110                 "top": value,
8111                 "bottom": value,
8112                 "z-index": "",
8113                 "position" : "static"
8114             });
8115             return this;
8116         },
8117
8118         /**
8119         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8120         * snapshot before performing an update and then restoring the element.
8121         * @return {Object}
8122         */
8123         getPositioning : function(){
8124             var l = this.getStyle("left");
8125             var t = this.getStyle("top");
8126             return {
8127                 "position" : this.getStyle("position"),
8128                 "left" : l,
8129                 "right" : l ? "" : this.getStyle("right"),
8130                 "top" : t,
8131                 "bottom" : t ? "" : this.getStyle("bottom"),
8132                 "z-index" : this.getStyle("z-index")
8133             };
8134         },
8135
8136         /**
8137          * Gets the width of the border(s) for the specified side(s)
8138          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8139          * passing lr would get the border (l)eft width + the border (r)ight width.
8140          * @return {Number} The width of the sides passed added together
8141          */
8142         getBorderWidth : function(side){
8143             return this.addStyles(side, El.borders);
8144         },
8145
8146         /**
8147          * Gets the width of the padding(s) for the specified side(s)
8148          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8149          * passing lr would get the padding (l)eft + the padding (r)ight.
8150          * @return {Number} The padding of the sides passed added together
8151          */
8152         getPadding : function(side){
8153             return this.addStyles(side, El.paddings);
8154         },
8155
8156         /**
8157         * Set positioning with an object returned by getPositioning().
8158         * @param {Object} posCfg
8159         * @return {Roo.Element} this
8160          */
8161         setPositioning : function(pc){
8162             this.applyStyles(pc);
8163             if(pc.right == "auto"){
8164                 this.dom.style.right = "";
8165             }
8166             if(pc.bottom == "auto"){
8167                 this.dom.style.bottom = "";
8168             }
8169             return this;
8170         },
8171
8172         // private
8173         fixDisplay : function(){
8174             if(this.getStyle("display") == "none"){
8175                 this.setStyle("visibility", "hidden");
8176                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8177                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8178                     this.setStyle("display", "block");
8179                 }
8180             }
8181         },
8182
8183         /**
8184          * Quick set left and top adding default units
8185          * @param {String} left The left CSS property value
8186          * @param {String} top The top CSS property value
8187          * @return {Roo.Element} this
8188          */
8189          setLeftTop : function(left, top){
8190             this.dom.style.left = this.addUnits(left);
8191             this.dom.style.top = this.addUnits(top);
8192             return this;
8193         },
8194
8195         /**
8196          * Move this element relative to its current position.
8197          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8198          * @param {Number} distance How far to move the element in pixels
8199          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8200          * @return {Roo.Element} this
8201          */
8202          move : function(direction, distance, animate){
8203             var xy = this.getXY();
8204             direction = direction.toLowerCase();
8205             switch(direction){
8206                 case "l":
8207                 case "left":
8208                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8209                     break;
8210                case "r":
8211                case "right":
8212                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8213                     break;
8214                case "t":
8215                case "top":
8216                case "up":
8217                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8218                     break;
8219                case "b":
8220                case "bottom":
8221                case "down":
8222                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8223                     break;
8224             }
8225             return this;
8226         },
8227
8228         /**
8229          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8230          * @return {Roo.Element} this
8231          */
8232         clip : function(){
8233             if(!this.isClipped){
8234                this.isClipped = true;
8235                this.originalClip = {
8236                    "o": this.getStyle("overflow"),
8237                    "x": this.getStyle("overflow-x"),
8238                    "y": this.getStyle("overflow-y")
8239                };
8240                this.setStyle("overflow", "hidden");
8241                this.setStyle("overflow-x", "hidden");
8242                this.setStyle("overflow-y", "hidden");
8243             }
8244             return this;
8245         },
8246
8247         /**
8248          *  Return clipping (overflow) to original clipping before clip() was called
8249          * @return {Roo.Element} this
8250          */
8251         unclip : function(){
8252             if(this.isClipped){
8253                 this.isClipped = false;
8254                 var o = this.originalClip;
8255                 if(o.o){this.setStyle("overflow", o.o);}
8256                 if(o.x){this.setStyle("overflow-x", o.x);}
8257                 if(o.y){this.setStyle("overflow-y", o.y);}
8258             }
8259             return this;
8260         },
8261
8262
8263         /**
8264          * Gets the x,y coordinates specified by the anchor position on the element.
8265          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8266          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8267          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8268          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8269          * @return {Array} [x, y] An array containing the element's x and y coordinates
8270          */
8271         getAnchorXY : function(anchor, local, s){
8272             //Passing a different size is useful for pre-calculating anchors,
8273             //especially for anchored animations that change the el size.
8274
8275             var w, h, vp = false;
8276             if(!s){
8277                 var d = this.dom;
8278                 if(d == document.body || d == document){
8279                     vp = true;
8280                     w = D.getViewWidth(); h = D.getViewHeight();
8281                 }else{
8282                     w = this.getWidth(); h = this.getHeight();
8283                 }
8284             }else{
8285                 w = s.width;  h = s.height;
8286             }
8287             var x = 0, y = 0, r = Math.round;
8288             switch((anchor || "tl").toLowerCase()){
8289                 case "c":
8290                     x = r(w*.5);
8291                     y = r(h*.5);
8292                 break;
8293                 case "t":
8294                     x = r(w*.5);
8295                     y = 0;
8296                 break;
8297                 case "l":
8298                     x = 0;
8299                     y = r(h*.5);
8300                 break;
8301                 case "r":
8302                     x = w;
8303                     y = r(h*.5);
8304                 break;
8305                 case "b":
8306                     x = r(w*.5);
8307                     y = h;
8308                 break;
8309                 case "tl":
8310                     x = 0;
8311                     y = 0;
8312                 break;
8313                 case "bl":
8314                     x = 0;
8315                     y = h;
8316                 break;
8317                 case "br":
8318                     x = w;
8319                     y = h;
8320                 break;
8321                 case "tr":
8322                     x = w;
8323                     y = 0;
8324                 break;
8325             }
8326             if(local === true){
8327                 return [x, y];
8328             }
8329             if(vp){
8330                 var sc = this.getScroll();
8331                 return [x + sc.left, y + sc.top];
8332             }
8333             //Add the element's offset xy
8334             var o = this.getXY();
8335             return [x+o[0], y+o[1]];
8336         },
8337
8338         /**
8339          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8340          * supported position values.
8341          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8342          * @param {String} position The position to align to.
8343          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8344          * @return {Array} [x, y]
8345          */
8346         getAlignToXY : function(el, p, o){
8347             el = Roo.get(el);
8348             var d = this.dom;
8349             if(!el.dom){
8350                 throw "Element.alignTo with an element that doesn't exist";
8351             }
8352             var c = false; //constrain to viewport
8353             var p1 = "", p2 = "";
8354             o = o || [0,0];
8355
8356             if(!p){
8357                 p = "tl-bl";
8358             }else if(p == "?"){
8359                 p = "tl-bl?";
8360             }else if(p.indexOf("-") == -1){
8361                 p = "tl-" + p;
8362             }
8363             p = p.toLowerCase();
8364             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8365             if(!m){
8366                throw "Element.alignTo with an invalid alignment " + p;
8367             }
8368             p1 = m[1]; p2 = m[2]; c = !!m[3];
8369
8370             //Subtract the aligned el's internal xy from the target's offset xy
8371             //plus custom offset to get the aligned el's new offset xy
8372             var a1 = this.getAnchorXY(p1, true);
8373             var a2 = el.getAnchorXY(p2, false);
8374             var x = a2[0] - a1[0] + o[0];
8375             var y = a2[1] - a1[1] + o[1];
8376             if(c){
8377                 //constrain the aligned el to viewport if necessary
8378                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8379                 // 5px of margin for ie
8380                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8381
8382                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8383                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8384                 //otherwise swap the aligned el to the opposite border of the target.
8385                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8386                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8387                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8388                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8389
8390                var doc = document;
8391                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8392                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8393
8394                if((x+w) > dw + scrollX){
8395                     x = swapX ? r.left-w : dw+scrollX-w;
8396                 }
8397                if(x < scrollX){
8398                    x = swapX ? r.right : scrollX;
8399                }
8400                if((y+h) > dh + scrollY){
8401                     y = swapY ? r.top-h : dh+scrollY-h;
8402                 }
8403                if (y < scrollY){
8404                    y = swapY ? r.bottom : scrollY;
8405                }
8406             }
8407             return [x,y];
8408         },
8409
8410         // private
8411         getConstrainToXY : function(){
8412             var os = {top:0, left:0, bottom:0, right: 0};
8413
8414             return function(el, local, offsets, proposedXY){
8415                 el = Roo.get(el);
8416                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8417
8418                 var vw, vh, vx = 0, vy = 0;
8419                 if(el.dom == document.body || el.dom == document){
8420                     vw = Roo.lib.Dom.getViewWidth();
8421                     vh = Roo.lib.Dom.getViewHeight();
8422                 }else{
8423                     vw = el.dom.clientWidth;
8424                     vh = el.dom.clientHeight;
8425                     if(!local){
8426                         var vxy = el.getXY();
8427                         vx = vxy[0];
8428                         vy = vxy[1];
8429                     }
8430                 }
8431
8432                 var s = el.getScroll();
8433
8434                 vx += offsets.left + s.left;
8435                 vy += offsets.top + s.top;
8436
8437                 vw -= offsets.right;
8438                 vh -= offsets.bottom;
8439
8440                 var vr = vx+vw;
8441                 var vb = vy+vh;
8442
8443                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8444                 var x = xy[0], y = xy[1];
8445                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8446
8447                 // only move it if it needs it
8448                 var moved = false;
8449
8450                 // first validate right/bottom
8451                 if((x + w) > vr){
8452                     x = vr - w;
8453                     moved = true;
8454                 }
8455                 if((y + h) > vb){
8456                     y = vb - h;
8457                     moved = true;
8458                 }
8459                 // then make sure top/left isn't negative
8460                 if(x < vx){
8461                     x = vx;
8462                     moved = true;
8463                 }
8464                 if(y < vy){
8465                     y = vy;
8466                     moved = true;
8467                 }
8468                 return moved ? [x, y] : false;
8469             };
8470         }(),
8471
8472         // private
8473         adjustForConstraints : function(xy, parent, offsets){
8474             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8475         },
8476
8477         /**
8478          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8479          * document it aligns it to the viewport.
8480          * The position parameter is optional, and can be specified in any one of the following formats:
8481          * <ul>
8482          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8483          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8484          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8485          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8486          *   <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
8487          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8488          * </ul>
8489          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8490          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8491          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8492          * that specified in order to enforce the viewport constraints.
8493          * Following are all of the supported anchor positions:
8494     <pre>
8495     Value  Description
8496     -----  -----------------------------
8497     tl     The top left corner (default)
8498     t      The center of the top edge
8499     tr     The top right corner
8500     l      The center of the left edge
8501     c      In the center of the element
8502     r      The center of the right edge
8503     bl     The bottom left corner
8504     b      The center of the bottom edge
8505     br     The bottom right corner
8506     </pre>
8507     Example Usage:
8508     <pre><code>
8509     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8510     el.alignTo("other-el");
8511
8512     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8513     el.alignTo("other-el", "tr?");
8514
8515     // align the bottom right corner of el with the center left edge of other-el
8516     el.alignTo("other-el", "br-l?");
8517
8518     // align the center of el with the bottom left corner of other-el and
8519     // adjust the x position by -6 pixels (and the y position by 0)
8520     el.alignTo("other-el", "c-bl", [-6, 0]);
8521     </code></pre>
8522          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8523          * @param {String} position The position to align to.
8524          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8525          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8526          * @return {Roo.Element} this
8527          */
8528         alignTo : function(element, position, offsets, animate){
8529             var xy = this.getAlignToXY(element, position, offsets);
8530             this.setXY(xy, this.preanim(arguments, 3));
8531             return this;
8532         },
8533
8534         /**
8535          * Anchors an element to another element and realigns it when the window is resized.
8536          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8537          * @param {String} position The position to align to.
8538          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8539          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8540          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8541          * is a number, it is used as the buffer delay (defaults to 50ms).
8542          * @param {Function} callback The function to call after the animation finishes
8543          * @return {Roo.Element} this
8544          */
8545         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8546             var action = function(){
8547                 this.alignTo(el, alignment, offsets, animate);
8548                 Roo.callback(callback, this);
8549             };
8550             Roo.EventManager.onWindowResize(action, this);
8551             var tm = typeof monitorScroll;
8552             if(tm != 'undefined'){
8553                 Roo.EventManager.on(window, 'scroll', action, this,
8554                     {buffer: tm == 'number' ? monitorScroll : 50});
8555             }
8556             action.call(this); // align immediately
8557             return this;
8558         },
8559         /**
8560          * Clears any opacity settings from this element. Required in some cases for IE.
8561          * @return {Roo.Element} this
8562          */
8563         clearOpacity : function(){
8564             if (window.ActiveXObject) {
8565                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8566                     this.dom.style.filter = "";
8567                 }
8568             } else {
8569                 this.dom.style.opacity = "";
8570                 this.dom.style["-moz-opacity"] = "";
8571                 this.dom.style["-khtml-opacity"] = "";
8572             }
8573             return this;
8574         },
8575
8576         /**
8577          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8578          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8579          * @return {Roo.Element} this
8580          */
8581         hide : function(animate){
8582             this.setVisible(false, this.preanim(arguments, 0));
8583             return this;
8584         },
8585
8586         /**
8587         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8588         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8589          * @return {Roo.Element} this
8590          */
8591         show : function(animate){
8592             this.setVisible(true, this.preanim(arguments, 0));
8593             return this;
8594         },
8595
8596         /**
8597          * @private Test if size has a unit, otherwise appends the default
8598          */
8599         addUnits : function(size){
8600             return Roo.Element.addUnits(size, this.defaultUnit);
8601         },
8602
8603         /**
8604          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8605          * @return {Roo.Element} this
8606          */
8607         beginMeasure : function(){
8608             var el = this.dom;
8609             if(el.offsetWidth || el.offsetHeight){
8610                 return this; // offsets work already
8611             }
8612             var changed = [];
8613             var p = this.dom, b = document.body; // start with this element
8614             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8615                 var pe = Roo.get(p);
8616                 if(pe.getStyle('display') == 'none'){
8617                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8618                     p.style.visibility = "hidden";
8619                     p.style.display = "block";
8620                 }
8621                 p = p.parentNode;
8622             }
8623             this._measureChanged = changed;
8624             return this;
8625
8626         },
8627
8628         /**
8629          * Restores displays to before beginMeasure was called
8630          * @return {Roo.Element} this
8631          */
8632         endMeasure : function(){
8633             var changed = this._measureChanged;
8634             if(changed){
8635                 for(var i = 0, len = changed.length; i < len; i++) {
8636                     var r = changed[i];
8637                     r.el.style.visibility = r.visibility;
8638                     r.el.style.display = "none";
8639                 }
8640                 this._measureChanged = null;
8641             }
8642             return this;
8643         },
8644
8645         /**
8646         * Update the innerHTML of this element, optionally searching for and processing scripts
8647         * @param {String} html The new HTML
8648         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8649         * @param {Function} callback For async script loading you can be noticed when the update completes
8650         * @return {Roo.Element} this
8651          */
8652         update : function(html, loadScripts, callback){
8653             if(typeof html == "undefined"){
8654                 html = "";
8655             }
8656             if(loadScripts !== true){
8657                 this.dom.innerHTML = html;
8658                 if(typeof callback == "function"){
8659                     callback();
8660                 }
8661                 return this;
8662             }
8663             var id = Roo.id();
8664             var dom = this.dom;
8665
8666             html += '<span id="' + id + '"></span>';
8667
8668             E.onAvailable(id, function(){
8669                 var hd = document.getElementsByTagName("head")[0];
8670                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8671                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8672                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8673
8674                 var match;
8675                 while(match = re.exec(html)){
8676                     var attrs = match[1];
8677                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8678                     if(srcMatch && srcMatch[2]){
8679                        var s = document.createElement("script");
8680                        s.src = srcMatch[2];
8681                        var typeMatch = attrs.match(typeRe);
8682                        if(typeMatch && typeMatch[2]){
8683                            s.type = typeMatch[2];
8684                        }
8685                        hd.appendChild(s);
8686                     }else if(match[2] && match[2].length > 0){
8687                         if(window.execScript) {
8688                            window.execScript(match[2]);
8689                         } else {
8690                             /**
8691                              * eval:var:id
8692                              * eval:var:dom
8693                              * eval:var:html
8694                              * 
8695                              */
8696                            window.eval(match[2]);
8697                         }
8698                     }
8699                 }
8700                 var el = document.getElementById(id);
8701                 if(el){el.parentNode.removeChild(el);}
8702                 if(typeof callback == "function"){
8703                     callback();
8704                 }
8705             });
8706             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8707             return this;
8708         },
8709
8710         /**
8711          * Direct access to the UpdateManager update() method (takes the same parameters).
8712          * @param {String/Function} url The url for this request or a function to call to get the url
8713          * @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}
8714          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8715          * @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.
8716          * @return {Roo.Element} this
8717          */
8718         load : function(){
8719             var um = this.getUpdateManager();
8720             um.update.apply(um, arguments);
8721             return this;
8722         },
8723
8724         /**
8725         * Gets this element's UpdateManager
8726         * @return {Roo.UpdateManager} The UpdateManager
8727         */
8728         getUpdateManager : function(){
8729             if(!this.updateManager){
8730                 this.updateManager = new Roo.UpdateManager(this);
8731             }
8732             return this.updateManager;
8733         },
8734
8735         /**
8736          * Disables text selection for this element (normalized across browsers)
8737          * @return {Roo.Element} this
8738          */
8739         unselectable : function(){
8740             this.dom.unselectable = "on";
8741             this.swallowEvent("selectstart", true);
8742             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8743             this.addClass("x-unselectable");
8744             return this;
8745         },
8746
8747         /**
8748         * Calculates the x, y to center this element on the screen
8749         * @return {Array} The x, y values [x, y]
8750         */
8751         getCenterXY : function(){
8752             return this.getAlignToXY(document, 'c-c');
8753         },
8754
8755         /**
8756         * Centers the Element in either the viewport, or another Element.
8757         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8758         */
8759         center : function(centerIn){
8760             this.alignTo(centerIn || document, 'c-c');
8761             return this;
8762         },
8763
8764         /**
8765          * Tests various css rules/browsers to determine if this element uses a border box
8766          * @return {Boolean}
8767          */
8768         isBorderBox : function(){
8769             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8770         },
8771
8772         /**
8773          * Return a box {x, y, width, height} that can be used to set another elements
8774          * size/location to match this element.
8775          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8776          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8777          * @return {Object} box An object in the format {x, y, width, height}
8778          */
8779         getBox : function(contentBox, local){
8780             var xy;
8781             if(!local){
8782                 xy = this.getXY();
8783             }else{
8784                 var left = parseInt(this.getStyle("left"), 10) || 0;
8785                 var top = parseInt(this.getStyle("top"), 10) || 0;
8786                 xy = [left, top];
8787             }
8788             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8789             if(!contentBox){
8790                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8791             }else{
8792                 var l = this.getBorderWidth("l")+this.getPadding("l");
8793                 var r = this.getBorderWidth("r")+this.getPadding("r");
8794                 var t = this.getBorderWidth("t")+this.getPadding("t");
8795                 var b = this.getBorderWidth("b")+this.getPadding("b");
8796                 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)};
8797             }
8798             bx.right = bx.x + bx.width;
8799             bx.bottom = bx.y + bx.height;
8800             return bx;
8801         },
8802
8803         /**
8804          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8805          for more information about the sides.
8806          * @param {String} sides
8807          * @return {Number}
8808          */
8809         getFrameWidth : function(sides, onlyContentBox){
8810             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8811         },
8812
8813         /**
8814          * 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.
8815          * @param {Object} box The box to fill {x, y, width, height}
8816          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8817          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8818          * @return {Roo.Element} this
8819          */
8820         setBox : function(box, adjust, animate){
8821             var w = box.width, h = box.height;
8822             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8823                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8824                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8825             }
8826             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8827             return this;
8828         },
8829
8830         /**
8831          * Forces the browser to repaint this element
8832          * @return {Roo.Element} this
8833          */
8834          repaint : function(){
8835             var dom = this.dom;
8836             this.addClass("x-repaint");
8837             setTimeout(function(){
8838                 Roo.get(dom).removeClass("x-repaint");
8839             }, 1);
8840             return this;
8841         },
8842
8843         /**
8844          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8845          * then it returns the calculated width of the sides (see getPadding)
8846          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8847          * @return {Object/Number}
8848          */
8849         getMargins : function(side){
8850             if(!side){
8851                 return {
8852                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8853                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8854                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8855                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8856                 };
8857             }else{
8858                 return this.addStyles(side, El.margins);
8859              }
8860         },
8861
8862         // private
8863         addStyles : function(sides, styles){
8864             var val = 0, v, w;
8865             for(var i = 0, len = sides.length; i < len; i++){
8866                 v = this.getStyle(styles[sides.charAt(i)]);
8867                 if(v){
8868                      w = parseInt(v, 10);
8869                      if(w){ val += w; }
8870                 }
8871             }
8872             return val;
8873         },
8874
8875         /**
8876          * Creates a proxy element of this element
8877          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8878          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8879          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8880          * @return {Roo.Element} The new proxy element
8881          */
8882         createProxy : function(config, renderTo, matchBox){
8883             if(renderTo){
8884                 renderTo = Roo.getDom(renderTo);
8885             }else{
8886                 renderTo = document.body;
8887             }
8888             config = typeof config == "object" ?
8889                 config : {tag : "div", cls: config};
8890             var proxy = Roo.DomHelper.append(renderTo, config, true);
8891             if(matchBox){
8892                proxy.setBox(this.getBox());
8893             }
8894             return proxy;
8895         },
8896
8897         /**
8898          * Puts a mask over this element to disable user interaction. Requires core.css.
8899          * This method can only be applied to elements which accept child nodes.
8900          * @param {String} msg (optional) A message to display in the mask
8901          * @param {String} msgCls (optional) A css class to apply to the msg element
8902          * @return {Element} The mask  element
8903          */
8904         mask : function(msg, msgCls)
8905         {
8906             if(this.getStyle("position") == "static"){
8907                 this.setStyle("position", "relative");
8908             }
8909             if(!this._mask){
8910                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8911             }
8912             this.addClass("x-masked");
8913             this._mask.setDisplayed(true);
8914             
8915             // we wander
8916             var z = 0;
8917             var dom = this.dom
8918             while (dom && dom.style) {
8919                 if (!isNaN(parseInt(dom.style.zIndex))) {
8920                     z = Math.max(z, parseInt(dom.style.zIndex));
8921                 }
8922                 dom = dom.parentNode;
8923             }
8924             // if we are masking the body - then it hides everything..
8925             if (this.dom == document.body) {
8926                 z = 1000000;
8927                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8928                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8929             }
8930            
8931             if(typeof msg == 'string'){
8932                 if(!this._maskMsg){
8933                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8934                 }
8935                 var mm = this._maskMsg;
8936                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8937                 mm.dom.firstChild.innerHTML = msg;
8938                 mm.setDisplayed(true);
8939                 mm.center(this);
8940                 mm.setStyle('z-index', z + 102);
8941             }
8942             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8943                 this._mask.setHeight(this.getHeight());
8944             }
8945             this._mask.setStyle('z-index', z + 100);
8946             
8947             return this._mask;
8948         },
8949
8950         /**
8951          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8952          * it is cached for reuse.
8953          */
8954         unmask : function(removeEl){
8955             if(this._mask){
8956                 if(removeEl === true){
8957                     this._mask.remove();
8958                     delete this._mask;
8959                     if(this._maskMsg){
8960                         this._maskMsg.remove();
8961                         delete this._maskMsg;
8962                     }
8963                 }else{
8964                     this._mask.setDisplayed(false);
8965                     if(this._maskMsg){
8966                         this._maskMsg.setDisplayed(false);
8967                     }
8968                 }
8969             }
8970             this.removeClass("x-masked");
8971         },
8972
8973         /**
8974          * Returns true if this element is masked
8975          * @return {Boolean}
8976          */
8977         isMasked : function(){
8978             return this._mask && this._mask.isVisible();
8979         },
8980
8981         /**
8982          * Creates an iframe shim for this element to keep selects and other windowed objects from
8983          * showing through.
8984          * @return {Roo.Element} The new shim element
8985          */
8986         createShim : function(){
8987             var el = document.createElement('iframe');
8988             el.frameBorder = 'no';
8989             el.className = 'roo-shim';
8990             if(Roo.isIE && Roo.isSecure){
8991                 el.src = Roo.SSL_SECURE_URL;
8992             }
8993             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8994             shim.autoBoxAdjust = false;
8995             return shim;
8996         },
8997
8998         /**
8999          * Removes this element from the DOM and deletes it from the cache
9000          */
9001         remove : function(){
9002             if(this.dom.parentNode){
9003                 this.dom.parentNode.removeChild(this.dom);
9004             }
9005             delete El.cache[this.dom.id];
9006         },
9007
9008         /**
9009          * Sets up event handlers to add and remove a css class when the mouse is over this element
9010          * @param {String} className
9011          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9012          * mouseout events for children elements
9013          * @return {Roo.Element} this
9014          */
9015         addClassOnOver : function(className, preventFlicker){
9016             this.on("mouseover", function(){
9017                 Roo.fly(this, '_internal').addClass(className);
9018             }, this.dom);
9019             var removeFn = function(e){
9020                 if(preventFlicker !== true || !e.within(this, true)){
9021                     Roo.fly(this, '_internal').removeClass(className);
9022                 }
9023             };
9024             this.on("mouseout", removeFn, this.dom);
9025             return this;
9026         },
9027
9028         /**
9029          * Sets up event handlers to add and remove a css class when this element has the focus
9030          * @param {String} className
9031          * @return {Roo.Element} this
9032          */
9033         addClassOnFocus : function(className){
9034             this.on("focus", function(){
9035                 Roo.fly(this, '_internal').addClass(className);
9036             }, this.dom);
9037             this.on("blur", function(){
9038                 Roo.fly(this, '_internal').removeClass(className);
9039             }, this.dom);
9040             return this;
9041         },
9042         /**
9043          * 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)
9044          * @param {String} className
9045          * @return {Roo.Element} this
9046          */
9047         addClassOnClick : function(className){
9048             var dom = this.dom;
9049             this.on("mousedown", function(){
9050                 Roo.fly(dom, '_internal').addClass(className);
9051                 var d = Roo.get(document);
9052                 var fn = function(){
9053                     Roo.fly(dom, '_internal').removeClass(className);
9054                     d.removeListener("mouseup", fn);
9055                 };
9056                 d.on("mouseup", fn);
9057             });
9058             return this;
9059         },
9060
9061         /**
9062          * Stops the specified event from bubbling and optionally prevents the default action
9063          * @param {String} eventName
9064          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9065          * @return {Roo.Element} this
9066          */
9067         swallowEvent : function(eventName, preventDefault){
9068             var fn = function(e){
9069                 e.stopPropagation();
9070                 if(preventDefault){
9071                     e.preventDefault();
9072                 }
9073             };
9074             if(eventName instanceof Array){
9075                 for(var i = 0, len = eventName.length; i < len; i++){
9076                      this.on(eventName[i], fn);
9077                 }
9078                 return this;
9079             }
9080             this.on(eventName, fn);
9081             return this;
9082         },
9083
9084         /**
9085          * @private
9086          */
9087       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9088
9089         /**
9090          * Sizes this element to its parent element's dimensions performing
9091          * neccessary box adjustments.
9092          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9093          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9094          * @return {Roo.Element} this
9095          */
9096         fitToParent : function(monitorResize, targetParent) {
9097           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9098           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9099           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9100             return;
9101           }
9102           var p = Roo.get(targetParent || this.dom.parentNode);
9103           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9104           if (monitorResize === true) {
9105             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9106             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9107           }
9108           return this;
9109         },
9110
9111         /**
9112          * Gets the next sibling, skipping text nodes
9113          * @return {HTMLElement} The next sibling or null
9114          */
9115         getNextSibling : function(){
9116             var n = this.dom.nextSibling;
9117             while(n && n.nodeType != 1){
9118                 n = n.nextSibling;
9119             }
9120             return n;
9121         },
9122
9123         /**
9124          * Gets the previous sibling, skipping text nodes
9125          * @return {HTMLElement} The previous sibling or null
9126          */
9127         getPrevSibling : function(){
9128             var n = this.dom.previousSibling;
9129             while(n && n.nodeType != 1){
9130                 n = n.previousSibling;
9131             }
9132             return n;
9133         },
9134
9135
9136         /**
9137          * Appends the passed element(s) to this element
9138          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9139          * @return {Roo.Element} this
9140          */
9141         appendChild: function(el){
9142             el = Roo.get(el);
9143             el.appendTo(this);
9144             return this;
9145         },
9146
9147         /**
9148          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9149          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9150          * automatically generated with the specified attributes.
9151          * @param {HTMLElement} insertBefore (optional) a child element of this element
9152          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9153          * @return {Roo.Element} The new child element
9154          */
9155         createChild: function(config, insertBefore, returnDom){
9156             config = config || {tag:'div'};
9157             if(insertBefore){
9158                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9159             }
9160             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9161         },
9162
9163         /**
9164          * Appends this element to the passed element
9165          * @param {String/HTMLElement/Element} el The new parent element
9166          * @return {Roo.Element} this
9167          */
9168         appendTo: function(el){
9169             el = Roo.getDom(el);
9170             el.appendChild(this.dom);
9171             return this;
9172         },
9173
9174         /**
9175          * Inserts this element before the passed element in the DOM
9176          * @param {String/HTMLElement/Element} el The element to insert before
9177          * @return {Roo.Element} this
9178          */
9179         insertBefore: function(el){
9180             el = Roo.getDom(el);
9181             el.parentNode.insertBefore(this.dom, el);
9182             return this;
9183         },
9184
9185         /**
9186          * Inserts this element after the passed element in the DOM
9187          * @param {String/HTMLElement/Element} el The element to insert after
9188          * @return {Roo.Element} this
9189          */
9190         insertAfter: function(el){
9191             el = Roo.getDom(el);
9192             el.parentNode.insertBefore(this.dom, el.nextSibling);
9193             return this;
9194         },
9195
9196         /**
9197          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9198          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9199          * @return {Roo.Element} The new child
9200          */
9201         insertFirst: function(el, returnDom){
9202             el = el || {};
9203             if(typeof el == 'object' && !el.nodeType){ // dh config
9204                 return this.createChild(el, this.dom.firstChild, returnDom);
9205             }else{
9206                 el = Roo.getDom(el);
9207                 this.dom.insertBefore(el, this.dom.firstChild);
9208                 return !returnDom ? Roo.get(el) : el;
9209             }
9210         },
9211
9212         /**
9213          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9214          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9215          * @param {String} where (optional) 'before' or 'after' defaults to before
9216          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9217          * @return {Roo.Element} the inserted Element
9218          */
9219         insertSibling: function(el, where, returnDom){
9220             where = where ? where.toLowerCase() : 'before';
9221             el = el || {};
9222             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9223
9224             if(typeof el == 'object' && !el.nodeType){ // dh config
9225                 if(where == 'after' && !this.dom.nextSibling){
9226                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9227                 }else{
9228                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9229                 }
9230
9231             }else{
9232                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9233                             where == 'before' ? this.dom : this.dom.nextSibling);
9234                 if(!returnDom){
9235                     rt = Roo.get(rt);
9236                 }
9237             }
9238             return rt;
9239         },
9240
9241         /**
9242          * Creates and wraps this element with another element
9243          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {HTMLElement/Element} The newly created wrapper element
9246          */
9247         wrap: function(config, returnDom){
9248             if(!config){
9249                 config = {tag: "div"};
9250             }
9251             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9252             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9253             return newEl;
9254         },
9255
9256         /**
9257          * Replaces the passed element with this element
9258          * @param {String/HTMLElement/Element} el The element to replace
9259          * @return {Roo.Element} this
9260          */
9261         replace: function(el){
9262             el = Roo.get(el);
9263             this.insertBefore(el);
9264             el.remove();
9265             return this;
9266         },
9267
9268         /**
9269          * Inserts an html fragment into this element
9270          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9271          * @param {String} html The HTML fragment
9272          * @param {Boolean} returnEl True to return an Roo.Element
9273          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9274          */
9275         insertHtml : function(where, html, returnEl){
9276             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9277             return returnEl ? Roo.get(el) : el;
9278         },
9279
9280         /**
9281          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9282          * @param {Object} o The object with the attributes
9283          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9284          * @return {Roo.Element} this
9285          */
9286         set : function(o, useSet){
9287             var el = this.dom;
9288             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9289             for(var attr in o){
9290                 if(attr == "style" || typeof o[attr] == "function") continue;
9291                 if(attr=="cls"){
9292                     el.className = o["cls"];
9293                 }else{
9294                     if(useSet) el.setAttribute(attr, o[attr]);
9295                     else el[attr] = o[attr];
9296                 }
9297             }
9298             if(o.style){
9299                 Roo.DomHelper.applyStyles(el, o.style);
9300             }
9301             return this;
9302         },
9303
9304         /**
9305          * Convenience method for constructing a KeyMap
9306          * @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:
9307          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9308          * @param {Function} fn The function to call
9309          * @param {Object} scope (optional) The scope of the function
9310          * @return {Roo.KeyMap} The KeyMap created
9311          */
9312         addKeyListener : function(key, fn, scope){
9313             var config;
9314             if(typeof key != "object" || key instanceof Array){
9315                 config = {
9316                     key: key,
9317                     fn: fn,
9318                     scope: scope
9319                 };
9320             }else{
9321                 config = {
9322                     key : key.key,
9323                     shift : key.shift,
9324                     ctrl : key.ctrl,
9325                     alt : key.alt,
9326                     fn: fn,
9327                     scope: scope
9328                 };
9329             }
9330             return new Roo.KeyMap(this, config);
9331         },
9332
9333         /**
9334          * Creates a KeyMap for this element
9335          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9336          * @return {Roo.KeyMap} The KeyMap created
9337          */
9338         addKeyMap : function(config){
9339             return new Roo.KeyMap(this, config);
9340         },
9341
9342         /**
9343          * Returns true if this element is scrollable.
9344          * @return {Boolean}
9345          */
9346          isScrollable : function(){
9347             var dom = this.dom;
9348             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9349         },
9350
9351         /**
9352          * 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().
9353          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9354          * @param {Number} value The new scroll value
9355          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9356          * @return {Element} this
9357          */
9358
9359         scrollTo : function(side, value, animate){
9360             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9361             if(!animate || !A){
9362                 this.dom[prop] = value;
9363             }else{
9364                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9365                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9366             }
9367             return this;
9368         },
9369
9370         /**
9371          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9372          * within this element's scrollable range.
9373          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9374          * @param {Number} distance How far to scroll the element in pixels
9375          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9376          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9377          * was scrolled as far as it could go.
9378          */
9379          scroll : function(direction, distance, animate){
9380              if(!this.isScrollable()){
9381                  return;
9382              }
9383              var el = this.dom;
9384              var l = el.scrollLeft, t = el.scrollTop;
9385              var w = el.scrollWidth, h = el.scrollHeight;
9386              var cw = el.clientWidth, ch = el.clientHeight;
9387              direction = direction.toLowerCase();
9388              var scrolled = false;
9389              var a = this.preanim(arguments, 2);
9390              switch(direction){
9391                  case "l":
9392                  case "left":
9393                      if(w - l > cw){
9394                          var v = Math.min(l + distance, w-cw);
9395                          this.scrollTo("left", v, a);
9396                          scrolled = true;
9397                      }
9398                      break;
9399                 case "r":
9400                 case "right":
9401                      if(l > 0){
9402                          var v = Math.max(l - distance, 0);
9403                          this.scrollTo("left", v, a);
9404                          scrolled = true;
9405                      }
9406                      break;
9407                 case "t":
9408                 case "top":
9409                 case "up":
9410                      if(t > 0){
9411                          var v = Math.max(t - distance, 0);
9412                          this.scrollTo("top", v, a);
9413                          scrolled = true;
9414                      }
9415                      break;
9416                 case "b":
9417                 case "bottom":
9418                 case "down":
9419                      if(h - t > ch){
9420                          var v = Math.min(t + distance, h-ch);
9421                          this.scrollTo("top", v, a);
9422                          scrolled = true;
9423                      }
9424                      break;
9425              }
9426              return scrolled;
9427         },
9428
9429         /**
9430          * Translates the passed page coordinates into left/top css values for this element
9431          * @param {Number/Array} x The page x or an array containing [x, y]
9432          * @param {Number} y The page y
9433          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9434          */
9435         translatePoints : function(x, y){
9436             if(typeof x == 'object' || x instanceof Array){
9437                 y = x[1]; x = x[0];
9438             }
9439             var p = this.getStyle('position');
9440             var o = this.getXY();
9441
9442             var l = parseInt(this.getStyle('left'), 10);
9443             var t = parseInt(this.getStyle('top'), 10);
9444
9445             if(isNaN(l)){
9446                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9447             }
9448             if(isNaN(t)){
9449                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9450             }
9451
9452             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9453         },
9454
9455         /**
9456          * Returns the current scroll position of the element.
9457          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9458          */
9459         getScroll : function(){
9460             var d = this.dom, doc = document;
9461             if(d == doc || d == doc.body){
9462                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9463                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9464                 return {left: l, top: t};
9465             }else{
9466                 return {left: d.scrollLeft, top: d.scrollTop};
9467             }
9468         },
9469
9470         /**
9471          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9472          * are convert to standard 6 digit hex color.
9473          * @param {String} attr The css attribute
9474          * @param {String} defaultValue The default value to use when a valid color isn't found
9475          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9476          * YUI color anims.
9477          */
9478         getColor : function(attr, defaultValue, prefix){
9479             var v = this.getStyle(attr);
9480             if(!v || v == "transparent" || v == "inherit") {
9481                 return defaultValue;
9482             }
9483             var color = typeof prefix == "undefined" ? "#" : prefix;
9484             if(v.substr(0, 4) == "rgb("){
9485                 var rvs = v.slice(4, v.length -1).split(",");
9486                 for(var i = 0; i < 3; i++){
9487                     var h = parseInt(rvs[i]).toString(16);
9488                     if(h < 16){
9489                         h = "0" + h;
9490                     }
9491                     color += h;
9492                 }
9493             } else {
9494                 if(v.substr(0, 1) == "#"){
9495                     if(v.length == 4) {
9496                         for(var i = 1; i < 4; i++){
9497                             var c = v.charAt(i);
9498                             color +=  c + c;
9499                         }
9500                     }else if(v.length == 7){
9501                         color += v.substr(1);
9502                     }
9503                 }
9504             }
9505             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9506         },
9507
9508         /**
9509          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9510          * gradient background, rounded corners and a 4-way shadow.
9511          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9512          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9513          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9514          * @return {Roo.Element} this
9515          */
9516         boxWrap : function(cls){
9517             cls = cls || 'x-box';
9518             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9519             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9520             return el;
9521         },
9522
9523         /**
9524          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9525          * @param {String} namespace The namespace in which to look for the attribute
9526          * @param {String} name The attribute name
9527          * @return {String} The attribute value
9528          */
9529         getAttributeNS : Roo.isIE ? function(ns, name){
9530             var d = this.dom;
9531             var type = typeof d[ns+":"+name];
9532             if(type != 'undefined' && type != 'unknown'){
9533                 return d[ns+":"+name];
9534             }
9535             return d[name];
9536         } : function(ns, name){
9537             var d = this.dom;
9538             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9539         }
9540     };
9541
9542     var ep = El.prototype;
9543
9544     /**
9545      * Appends an event handler (Shorthand for addListener)
9546      * @param {String}   eventName     The type of event to append
9547      * @param {Function} fn        The method the event invokes
9548      * @param {Object} scope       (optional) The scope (this object) of the fn
9549      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9550      * @method
9551      */
9552     ep.on = ep.addListener;
9553         // backwards compat
9554     ep.mon = ep.addListener;
9555
9556     /**
9557      * Removes an event handler from this element (shorthand for removeListener)
9558      * @param {String} eventName the type of event to remove
9559      * @param {Function} fn the method the event invokes
9560      * @return {Roo.Element} this
9561      * @method
9562      */
9563     ep.un = ep.removeListener;
9564
9565     /**
9566      * true to automatically adjust width and height settings for box-model issues (default to true)
9567      */
9568     ep.autoBoxAdjust = true;
9569
9570     // private
9571     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9572
9573     // private
9574     El.addUnits = function(v, defaultUnit){
9575         if(v === "" || v == "auto"){
9576             return v;
9577         }
9578         if(v === undefined){
9579             return '';
9580         }
9581         if(typeof v == "number" || !El.unitPattern.test(v)){
9582             return v + (defaultUnit || 'px');
9583         }
9584         return v;
9585     };
9586
9587     // special markup used throughout Roo when box wrapping elements
9588     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>';
9589     /**
9590      * Visibility mode constant - Use visibility to hide element
9591      * @static
9592      * @type Number
9593      */
9594     El.VISIBILITY = 1;
9595     /**
9596      * Visibility mode constant - Use display to hide element
9597      * @static
9598      * @type Number
9599      */
9600     El.DISPLAY = 2;
9601
9602     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9603     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9604     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9605
9606
9607
9608     /**
9609      * @private
9610      */
9611     El.cache = {};
9612
9613     var docEl;
9614
9615     /**
9616      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9617      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9618      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9619      * @return {Element} The Element object
9620      * @static
9621      */
9622     El.get = function(el){
9623         var ex, elm, id;
9624         if(!el){ return null; }
9625         if(typeof el == "string"){ // element id
9626             if(!(elm = document.getElementById(el))){
9627                 return null;
9628             }
9629             if(ex = El.cache[el]){
9630                 ex.dom = elm;
9631             }else{
9632                 ex = El.cache[el] = new El(elm);
9633             }
9634             return ex;
9635         }else if(el.tagName){ // dom element
9636             if(!(id = el.id)){
9637                 id = Roo.id(el);
9638             }
9639             if(ex = El.cache[id]){
9640                 ex.dom = el;
9641             }else{
9642                 ex = El.cache[id] = new El(el);
9643             }
9644             return ex;
9645         }else if(el instanceof El){
9646             if(el != docEl){
9647                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9648                                                               // catch case where it hasn't been appended
9649                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9650             }
9651             return el;
9652         }else if(el.isComposite){
9653             return el;
9654         }else if(el instanceof Array){
9655             return El.select(el);
9656         }else if(el == document){
9657             // create a bogus element object representing the document object
9658             if(!docEl){
9659                 var f = function(){};
9660                 f.prototype = El.prototype;
9661                 docEl = new f();
9662                 docEl.dom = document;
9663             }
9664             return docEl;
9665         }
9666         return null;
9667     };
9668
9669     // private
9670     El.uncache = function(el){
9671         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9672             if(a[i]){
9673                 delete El.cache[a[i].id || a[i]];
9674             }
9675         }
9676     };
9677
9678     // private
9679     // Garbage collection - uncache elements/purge listeners on orphaned elements
9680     // so we don't hold a reference and cause the browser to retain them
9681     El.garbageCollect = function(){
9682         if(!Roo.enableGarbageCollector){
9683             clearInterval(El.collectorThread);
9684             return;
9685         }
9686         for(var eid in El.cache){
9687             var el = El.cache[eid], d = el.dom;
9688             // -------------------------------------------------------
9689             // Determining what is garbage:
9690             // -------------------------------------------------------
9691             // !d
9692             // dom node is null, definitely garbage
9693             // -------------------------------------------------------
9694             // !d.parentNode
9695             // no parentNode == direct orphan, definitely garbage
9696             // -------------------------------------------------------
9697             // !d.offsetParent && !document.getElementById(eid)
9698             // display none elements have no offsetParent so we will
9699             // also try to look it up by it's id. However, check
9700             // offsetParent first so we don't do unneeded lookups.
9701             // This enables collection of elements that are not orphans
9702             // directly, but somewhere up the line they have an orphan
9703             // parent.
9704             // -------------------------------------------------------
9705             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9706                 delete El.cache[eid];
9707                 if(d && Roo.enableListenerCollection){
9708                     E.purgeElement(d);
9709                 }
9710             }
9711         }
9712     }
9713     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9714
9715
9716     // dom is optional
9717     El.Flyweight = function(dom){
9718         this.dom = dom;
9719     };
9720     El.Flyweight.prototype = El.prototype;
9721
9722     El._flyweights = {};
9723     /**
9724      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9725      * the dom node can be overwritten by other code.
9726      * @param {String/HTMLElement} el The dom node or id
9727      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9728      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9729      * @static
9730      * @return {Element} The shared Element object
9731      */
9732     El.fly = function(el, named){
9733         named = named || '_global';
9734         el = Roo.getDom(el);
9735         if(!el){
9736             return null;
9737         }
9738         if(!El._flyweights[named]){
9739             El._flyweights[named] = new El.Flyweight();
9740         }
9741         El._flyweights[named].dom = el;
9742         return El._flyweights[named];
9743     };
9744
9745     /**
9746      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9747      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9748      * Shorthand of {@link Roo.Element#get}
9749      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9750      * @return {Element} The Element object
9751      * @member Roo
9752      * @method get
9753      */
9754     Roo.get = El.get;
9755     /**
9756      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9757      * the dom node can be overwritten by other code.
9758      * Shorthand of {@link Roo.Element#fly}
9759      * @param {String/HTMLElement} el The dom node or id
9760      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9761      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9762      * @static
9763      * @return {Element} The shared Element object
9764      * @member Roo
9765      * @method fly
9766      */
9767     Roo.fly = El.fly;
9768
9769     // speedy lookup for elements never to box adjust
9770     var noBoxAdjust = Roo.isStrict ? {
9771         select:1
9772     } : {
9773         input:1, select:1, textarea:1
9774     };
9775     if(Roo.isIE || Roo.isGecko){
9776         noBoxAdjust['button'] = 1;
9777     }
9778
9779
9780     Roo.EventManager.on(window, 'unload', function(){
9781         delete El.cache;
9782         delete El._flyweights;
9783     });
9784 })();
9785
9786
9787
9788
9789 if(Roo.DomQuery){
9790     Roo.Element.selectorFunction = Roo.DomQuery.select;
9791 }
9792
9793 Roo.Element.select = function(selector, unique, root){
9794     var els;
9795     if(typeof selector == "string"){
9796         els = Roo.Element.selectorFunction(selector, root);
9797     }else if(selector.length !== undefined){
9798         els = selector;
9799     }else{
9800         throw "Invalid selector";
9801     }
9802     if(unique === true){
9803         return new Roo.CompositeElement(els);
9804     }else{
9805         return new Roo.CompositeElementLite(els);
9806     }
9807 };
9808 /**
9809  * Selects elements based on the passed CSS selector to enable working on them as 1.
9810  * @param {String/Array} selector The CSS selector or an array of elements
9811  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9812  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9813  * @return {CompositeElementLite/CompositeElement}
9814  * @member Roo
9815  * @method select
9816  */
9817 Roo.select = Roo.Element.select;
9818
9819
9820
9821
9822
9823
9824
9825
9826
9827
9828
9829
9830
9831
9832 /*
9833  * Based on:
9834  * Ext JS Library 1.1.1
9835  * Copyright(c) 2006-2007, Ext JS, LLC.
9836  *
9837  * Originally Released Under LGPL - original licence link has changed is not relivant.
9838  *
9839  * Fork - LGPL
9840  * <script type="text/javascript">
9841  */
9842
9843
9844
9845 //Notifies Element that fx methods are available
9846 Roo.enableFx = true;
9847
9848 /**
9849  * @class Roo.Fx
9850  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9851  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9852  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9853  * Element effects to work.</p><br/>
9854  *
9855  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9856  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9857  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9858  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9859  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9860  * expected results and should be done with care.</p><br/>
9861  *
9862  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9863  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9864 <pre>
9865 Value  Description
9866 -----  -----------------------------
9867 tl     The top left corner
9868 t      The center of the top edge
9869 tr     The top right corner
9870 l      The center of the left edge
9871 r      The center of the right edge
9872 bl     The bottom left corner
9873 b      The center of the bottom edge
9874 br     The bottom right corner
9875 </pre>
9876  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9877  * below are common options that can be passed to any Fx method.</b>
9878  * @cfg {Function} callback A function called when the effect is finished
9879  * @cfg {Object} scope The scope of the effect function
9880  * @cfg {String} easing A valid Easing value for the effect
9881  * @cfg {String} afterCls A css class to apply after the effect
9882  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9883  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9884  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9885  * effects that end with the element being visually hidden, ignored otherwise)
9886  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9887  * a function which returns such a specification that will be applied to the Element after the effect finishes
9888  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9889  * @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
9890  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9891  */
9892 Roo.Fx = {
9893         /**
9894          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9895          * origin for the slide effect.  This function automatically handles wrapping the element with
9896          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9897          * Usage:
9898          *<pre><code>
9899 // default: slide the element in from the top
9900 el.slideIn();
9901
9902 // custom: slide the element in from the right with a 2-second duration
9903 el.slideIn('r', { duration: 2 });
9904
9905 // common config options shown with default values
9906 el.slideIn('t', {
9907     easing: 'easeOut',
9908     duration: .5
9909 });
9910 </code></pre>
9911          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9912          * @param {Object} options (optional) Object literal with any of the Fx config options
9913          * @return {Roo.Element} The Element
9914          */
9915     slideIn : function(anchor, o){
9916         var el = this.getFxEl();
9917         o = o || {};
9918
9919         el.queueFx(o, function(){
9920
9921             anchor = anchor || "t";
9922
9923             // fix display to visibility
9924             this.fixDisplay();
9925
9926             // restore values after effect
9927             var r = this.getFxRestore();
9928             var b = this.getBox();
9929             // fixed size for slide
9930             this.setSize(b);
9931
9932             // wrap if needed
9933             var wrap = this.fxWrap(r.pos, o, "hidden");
9934
9935             var st = this.dom.style;
9936             st.visibility = "visible";
9937             st.position = "absolute";
9938
9939             // clear out temp styles after slide and unwrap
9940             var after = function(){
9941                 el.fxUnwrap(wrap, r.pos, o);
9942                 st.width = r.width;
9943                 st.height = r.height;
9944                 el.afterFx(o);
9945             };
9946             // time to calc the positions
9947             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9948
9949             switch(anchor.toLowerCase()){
9950                 case "t":
9951                     wrap.setSize(b.width, 0);
9952                     st.left = st.bottom = "0";
9953                     a = {height: bh};
9954                 break;
9955                 case "l":
9956                     wrap.setSize(0, b.height);
9957                     st.right = st.top = "0";
9958                     a = {width: bw};
9959                 break;
9960                 case "r":
9961                     wrap.setSize(0, b.height);
9962                     wrap.setX(b.right);
9963                     st.left = st.top = "0";
9964                     a = {width: bw, points: pt};
9965                 break;
9966                 case "b":
9967                     wrap.setSize(b.width, 0);
9968                     wrap.setY(b.bottom);
9969                     st.left = st.top = "0";
9970                     a = {height: bh, points: pt};
9971                 break;
9972                 case "tl":
9973                     wrap.setSize(0, 0);
9974                     st.right = st.bottom = "0";
9975                     a = {width: bw, height: bh};
9976                 break;
9977                 case "bl":
9978                     wrap.setSize(0, 0);
9979                     wrap.setY(b.y+b.height);
9980                     st.right = st.top = "0";
9981                     a = {width: bw, height: bh, points: pt};
9982                 break;
9983                 case "br":
9984                     wrap.setSize(0, 0);
9985                     wrap.setXY([b.right, b.bottom]);
9986                     st.left = st.top = "0";
9987                     a = {width: bw, height: bh, points: pt};
9988                 break;
9989                 case "tr":
9990                     wrap.setSize(0, 0);
9991                     wrap.setX(b.x+b.width);
9992                     st.left = st.bottom = "0";
9993                     a = {width: bw, height: bh, points: pt};
9994                 break;
9995             }
9996             this.dom.style.visibility = "visible";
9997             wrap.show();
9998
9999             arguments.callee.anim = wrap.fxanim(a,
10000                 o,
10001                 'motion',
10002                 .5,
10003                 'easeOut', after);
10004         });
10005         return this;
10006     },
10007     
10008         /**
10009          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10010          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10011          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10012          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10013          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10014          * Usage:
10015          *<pre><code>
10016 // default: slide the element out to the top
10017 el.slideOut();
10018
10019 // custom: slide the element out to the right with a 2-second duration
10020 el.slideOut('r', { duration: 2 });
10021
10022 // common config options shown with default values
10023 el.slideOut('t', {
10024     easing: 'easeOut',
10025     duration: .5,
10026     remove: false,
10027     useDisplay: false
10028 });
10029 </code></pre>
10030          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10031          * @param {Object} options (optional) Object literal with any of the Fx config options
10032          * @return {Roo.Element} The Element
10033          */
10034     slideOut : function(anchor, o){
10035         var el = this.getFxEl();
10036         o = o || {};
10037
10038         el.queueFx(o, function(){
10039
10040             anchor = anchor || "t";
10041
10042             // restore values after effect
10043             var r = this.getFxRestore();
10044             
10045             var b = this.getBox();
10046             // fixed size for slide
10047             this.setSize(b);
10048
10049             // wrap if needed
10050             var wrap = this.fxWrap(r.pos, o, "visible");
10051
10052             var st = this.dom.style;
10053             st.visibility = "visible";
10054             st.position = "absolute";
10055
10056             wrap.setSize(b);
10057
10058             var after = function(){
10059                 if(o.useDisplay){
10060                     el.setDisplayed(false);
10061                 }else{
10062                     el.hide();
10063                 }
10064
10065                 el.fxUnwrap(wrap, r.pos, o);
10066
10067                 st.width = r.width;
10068                 st.height = r.height;
10069
10070                 el.afterFx(o);
10071             };
10072
10073             var a, zero = {to: 0};
10074             switch(anchor.toLowerCase()){
10075                 case "t":
10076                     st.left = st.bottom = "0";
10077                     a = {height: zero};
10078                 break;
10079                 case "l":
10080                     st.right = st.top = "0";
10081                     a = {width: zero};
10082                 break;
10083                 case "r":
10084                     st.left = st.top = "0";
10085                     a = {width: zero, points: {to:[b.right, b.y]}};
10086                 break;
10087                 case "b":
10088                     st.left = st.top = "0";
10089                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10090                 break;
10091                 case "tl":
10092                     st.right = st.bottom = "0";
10093                     a = {width: zero, height: zero};
10094                 break;
10095                 case "bl":
10096                     st.right = st.top = "0";
10097                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10098                 break;
10099                 case "br":
10100                     st.left = st.top = "0";
10101                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10102                 break;
10103                 case "tr":
10104                     st.left = st.bottom = "0";
10105                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10106                 break;
10107             }
10108
10109             arguments.callee.anim = wrap.fxanim(a,
10110                 o,
10111                 'motion',
10112                 .5,
10113                 "easeOut", after);
10114         });
10115         return this;
10116     },
10117
10118         /**
10119          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10120          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10121          * The element must be removed from the DOM using the 'remove' config option if desired.
10122          * Usage:
10123          *<pre><code>
10124 // default
10125 el.puff();
10126
10127 // common config options shown with default values
10128 el.puff({
10129     easing: 'easeOut',
10130     duration: .5,
10131     remove: false,
10132     useDisplay: false
10133 });
10134 </code></pre>
10135          * @param {Object} options (optional) Object literal with any of the Fx config options
10136          * @return {Roo.Element} The Element
10137          */
10138     puff : function(o){
10139         var el = this.getFxEl();
10140         o = o || {};
10141
10142         el.queueFx(o, function(){
10143             this.clearOpacity();
10144             this.show();
10145
10146             // restore values after effect
10147             var r = this.getFxRestore();
10148             var st = this.dom.style;
10149
10150             var after = function(){
10151                 if(o.useDisplay){
10152                     el.setDisplayed(false);
10153                 }else{
10154                     el.hide();
10155                 }
10156
10157                 el.clearOpacity();
10158
10159                 el.setPositioning(r.pos);
10160                 st.width = r.width;
10161                 st.height = r.height;
10162                 st.fontSize = '';
10163                 el.afterFx(o);
10164             };
10165
10166             var width = this.getWidth();
10167             var height = this.getHeight();
10168
10169             arguments.callee.anim = this.fxanim({
10170                     width : {to: this.adjustWidth(width * 2)},
10171                     height : {to: this.adjustHeight(height * 2)},
10172                     points : {by: [-(width * .5), -(height * .5)]},
10173                     opacity : {to: 0},
10174                     fontSize: {to:200, unit: "%"}
10175                 },
10176                 o,
10177                 'motion',
10178                 .5,
10179                 "easeOut", after);
10180         });
10181         return this;
10182     },
10183
10184         /**
10185          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10186          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10187          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10188          * Usage:
10189          *<pre><code>
10190 // default
10191 el.switchOff();
10192
10193 // all config options shown with default values
10194 el.switchOff({
10195     easing: 'easeIn',
10196     duration: .3,
10197     remove: false,
10198     useDisplay: false
10199 });
10200 </code></pre>
10201          * @param {Object} options (optional) Object literal with any of the Fx config options
10202          * @return {Roo.Element} The Element
10203          */
10204     switchOff : function(o){
10205         var el = this.getFxEl();
10206         o = o || {};
10207
10208         el.queueFx(o, function(){
10209             this.clearOpacity();
10210             this.clip();
10211
10212             // restore values after effect
10213             var r = this.getFxRestore();
10214             var st = this.dom.style;
10215
10216             var after = function(){
10217                 if(o.useDisplay){
10218                     el.setDisplayed(false);
10219                 }else{
10220                     el.hide();
10221                 }
10222
10223                 el.clearOpacity();
10224                 el.setPositioning(r.pos);
10225                 st.width = r.width;
10226                 st.height = r.height;
10227
10228                 el.afterFx(o);
10229             };
10230
10231             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10232                 this.clearOpacity();
10233                 (function(){
10234                     this.fxanim({
10235                         height:{to:1},
10236                         points:{by:[0, this.getHeight() * .5]}
10237                     }, o, 'motion', 0.3, 'easeIn', after);
10238                 }).defer(100, this);
10239             });
10240         });
10241         return this;
10242     },
10243
10244     /**
10245      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10246      * changed using the "attr" config option) and then fading back to the original color. If no original
10247      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10248      * Usage:
10249 <pre><code>
10250 // default: highlight background to yellow
10251 el.highlight();
10252
10253 // custom: highlight foreground text to blue for 2 seconds
10254 el.highlight("0000ff", { attr: 'color', duration: 2 });
10255
10256 // common config options shown with default values
10257 el.highlight("ffff9c", {
10258     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10259     endColor: (current color) or "ffffff",
10260     easing: 'easeIn',
10261     duration: 1
10262 });
10263 </code></pre>
10264      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10265      * @param {Object} options (optional) Object literal with any of the Fx config options
10266      * @return {Roo.Element} The Element
10267      */ 
10268     highlight : function(color, o){
10269         var el = this.getFxEl();
10270         o = o || {};
10271
10272         el.queueFx(o, function(){
10273             color = color || "ffff9c";
10274             attr = o.attr || "backgroundColor";
10275
10276             this.clearOpacity();
10277             this.show();
10278
10279             var origColor = this.getColor(attr);
10280             var restoreColor = this.dom.style[attr];
10281             endColor = (o.endColor || origColor) || "ffffff";
10282
10283             var after = function(){
10284                 el.dom.style[attr] = restoreColor;
10285                 el.afterFx(o);
10286             };
10287
10288             var a = {};
10289             a[attr] = {from: color, to: endColor};
10290             arguments.callee.anim = this.fxanim(a,
10291                 o,
10292                 'color',
10293                 1,
10294                 'easeIn', after);
10295         });
10296         return this;
10297     },
10298
10299    /**
10300     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10301     * Usage:
10302 <pre><code>
10303 // default: a single light blue ripple
10304 el.frame();
10305
10306 // custom: 3 red ripples lasting 3 seconds total
10307 el.frame("ff0000", 3, { duration: 3 });
10308
10309 // common config options shown with default values
10310 el.frame("C3DAF9", 1, {
10311     duration: 1 //duration of entire animation (not each individual ripple)
10312     // Note: Easing is not configurable and will be ignored if included
10313 });
10314 </code></pre>
10315     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10316     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10317     * @param {Object} options (optional) Object literal with any of the Fx config options
10318     * @return {Roo.Element} The Element
10319     */
10320     frame : function(color, count, o){
10321         var el = this.getFxEl();
10322         o = o || {};
10323
10324         el.queueFx(o, function(){
10325             color = color || "#C3DAF9";
10326             if(color.length == 6){
10327                 color = "#" + color;
10328             }
10329             count = count || 1;
10330             duration = o.duration || 1;
10331             this.show();
10332
10333             var b = this.getBox();
10334             var animFn = function(){
10335                 var proxy = this.createProxy({
10336
10337                      style:{
10338                         visbility:"hidden",
10339                         position:"absolute",
10340                         "z-index":"35000", // yee haw
10341                         border:"0px solid " + color
10342                      }
10343                   });
10344                 var scale = Roo.isBorderBox ? 2 : 1;
10345                 proxy.animate({
10346                     top:{from:b.y, to:b.y - 20},
10347                     left:{from:b.x, to:b.x - 20},
10348                     borderWidth:{from:0, to:10},
10349                     opacity:{from:1, to:0},
10350                     height:{from:b.height, to:(b.height + (20*scale))},
10351                     width:{from:b.width, to:(b.width + (20*scale))}
10352                 }, duration, function(){
10353                     proxy.remove();
10354                 });
10355                 if(--count > 0){
10356                      animFn.defer((duration/2)*1000, this);
10357                 }else{
10358                     el.afterFx(o);
10359                 }
10360             };
10361             animFn.call(this);
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Creates a pause before any subsequent queued effects begin.  If there are
10368     * no effects queued after the pause it will have no effect.
10369     * Usage:
10370 <pre><code>
10371 el.pause(1);
10372 </code></pre>
10373     * @param {Number} seconds The length of time to pause (in seconds)
10374     * @return {Roo.Element} The Element
10375     */
10376     pause : function(seconds){
10377         var el = this.getFxEl();
10378         var o = {};
10379
10380         el.queueFx(o, function(){
10381             setTimeout(function(){
10382                 el.afterFx(o);
10383             }, seconds * 1000);
10384         });
10385         return this;
10386     },
10387
10388    /**
10389     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10390     * using the "endOpacity" config option.
10391     * Usage:
10392 <pre><code>
10393 // default: fade in from opacity 0 to 100%
10394 el.fadeIn();
10395
10396 // custom: fade in from opacity 0 to 75% over 2 seconds
10397 el.fadeIn({ endOpacity: .75, duration: 2});
10398
10399 // common config options shown with default values
10400 el.fadeIn({
10401     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10402     easing: 'easeOut',
10403     duration: .5
10404 });
10405 </code></pre>
10406     * @param {Object} options (optional) Object literal with any of the Fx config options
10407     * @return {Roo.Element} The Element
10408     */
10409     fadeIn : function(o){
10410         var el = this.getFxEl();
10411         o = o || {};
10412         el.queueFx(o, function(){
10413             this.setOpacity(0);
10414             this.fixDisplay();
10415             this.dom.style.visibility = 'visible';
10416             var to = o.endOpacity || 1;
10417             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10418                 o, null, .5, "easeOut", function(){
10419                 if(to == 1){
10420                     this.clearOpacity();
10421                 }
10422                 el.afterFx(o);
10423             });
10424         });
10425         return this;
10426     },
10427
10428    /**
10429     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10430     * using the "endOpacity" config option.
10431     * Usage:
10432 <pre><code>
10433 // default: fade out from the element's current opacity to 0
10434 el.fadeOut();
10435
10436 // custom: fade out from the element's current opacity to 25% over 2 seconds
10437 el.fadeOut({ endOpacity: .25, duration: 2});
10438
10439 // common config options shown with default values
10440 el.fadeOut({
10441     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10442     easing: 'easeOut',
10443     duration: .5
10444     remove: false,
10445     useDisplay: false
10446 });
10447 </code></pre>
10448     * @param {Object} options (optional) Object literal with any of the Fx config options
10449     * @return {Roo.Element} The Element
10450     */
10451     fadeOut : function(o){
10452         var el = this.getFxEl();
10453         o = o || {};
10454         el.queueFx(o, function(){
10455             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10456                 o, null, .5, "easeOut", function(){
10457                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10458                      this.dom.style.display = "none";
10459                 }else{
10460                      this.dom.style.visibility = "hidden";
10461                 }
10462                 this.clearOpacity();
10463                 el.afterFx(o);
10464             });
10465         });
10466         return this;
10467     },
10468
10469    /**
10470     * Animates the transition of an element's dimensions from a starting height/width
10471     * to an ending height/width.
10472     * Usage:
10473 <pre><code>
10474 // change height and width to 100x100 pixels
10475 el.scale(100, 100);
10476
10477 // common config options shown with default values.  The height and width will default to
10478 // the element's existing values if passed as null.
10479 el.scale(
10480     [element's width],
10481     [element's height], {
10482     easing: 'easeOut',
10483     duration: .35
10484 });
10485 </code></pre>
10486     * @param {Number} width  The new width (pass undefined to keep the original width)
10487     * @param {Number} height  The new height (pass undefined to keep the original height)
10488     * @param {Object} options (optional) Object literal with any of the Fx config options
10489     * @return {Roo.Element} The Element
10490     */
10491     scale : function(w, h, o){
10492         this.shift(Roo.apply({}, o, {
10493             width: w,
10494             height: h
10495         }));
10496         return this;
10497     },
10498
10499    /**
10500     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10501     * Any of these properties not specified in the config object will not be changed.  This effect 
10502     * requires that at least one new dimension, position or opacity setting must be passed in on
10503     * the config object in order for the function to have any effect.
10504     * Usage:
10505 <pre><code>
10506 // slide the element horizontally to x position 200 while changing the height and opacity
10507 el.shift({ x: 200, height: 50, opacity: .8 });
10508
10509 // common config options shown with default values.
10510 el.shift({
10511     width: [element's width],
10512     height: [element's height],
10513     x: [element's x position],
10514     y: [element's y position],
10515     opacity: [element's opacity],
10516     easing: 'easeOut',
10517     duration: .35
10518 });
10519 </code></pre>
10520     * @param {Object} options  Object literal with any of the Fx config options
10521     * @return {Roo.Element} The Element
10522     */
10523     shift : function(o){
10524         var el = this.getFxEl();
10525         o = o || {};
10526         el.queueFx(o, function(){
10527             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10528             if(w !== undefined){
10529                 a.width = {to: this.adjustWidth(w)};
10530             }
10531             if(h !== undefined){
10532                 a.height = {to: this.adjustHeight(h)};
10533             }
10534             if(x !== undefined || y !== undefined){
10535                 a.points = {to: [
10536                     x !== undefined ? x : this.getX(),
10537                     y !== undefined ? y : this.getY()
10538                 ]};
10539             }
10540             if(op !== undefined){
10541                 a.opacity = {to: op};
10542             }
10543             if(o.xy !== undefined){
10544                 a.points = {to: o.xy};
10545             }
10546             arguments.callee.anim = this.fxanim(a,
10547                 o, 'motion', .35, "easeOut", function(){
10548                 el.afterFx(o);
10549             });
10550         });
10551         return this;
10552     },
10553
10554         /**
10555          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10556          * ending point of the effect.
10557          * Usage:
10558          *<pre><code>
10559 // default: slide the element downward while fading out
10560 el.ghost();
10561
10562 // custom: slide the element out to the right with a 2-second duration
10563 el.ghost('r', { duration: 2 });
10564
10565 // common config options shown with default values
10566 el.ghost('b', {
10567     easing: 'easeOut',
10568     duration: .5
10569     remove: false,
10570     useDisplay: false
10571 });
10572 </code></pre>
10573          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10574          * @param {Object} options (optional) Object literal with any of the Fx config options
10575          * @return {Roo.Element} The Element
10576          */
10577     ghost : function(anchor, o){
10578         var el = this.getFxEl();
10579         o = o || {};
10580
10581         el.queueFx(o, function(){
10582             anchor = anchor || "b";
10583
10584             // restore values after effect
10585             var r = this.getFxRestore();
10586             var w = this.getWidth(),
10587                 h = this.getHeight();
10588
10589             var st = this.dom.style;
10590
10591             var after = function(){
10592                 if(o.useDisplay){
10593                     el.setDisplayed(false);
10594                 }else{
10595                     el.hide();
10596                 }
10597
10598                 el.clearOpacity();
10599                 el.setPositioning(r.pos);
10600                 st.width = r.width;
10601                 st.height = r.height;
10602
10603                 el.afterFx(o);
10604             };
10605
10606             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10607             switch(anchor.toLowerCase()){
10608                 case "t":
10609                     pt.by = [0, -h];
10610                 break;
10611                 case "l":
10612                     pt.by = [-w, 0];
10613                 break;
10614                 case "r":
10615                     pt.by = [w, 0];
10616                 break;
10617                 case "b":
10618                     pt.by = [0, h];
10619                 break;
10620                 case "tl":
10621                     pt.by = [-w, -h];
10622                 break;
10623                 case "bl":
10624                     pt.by = [-w, h];
10625                 break;
10626                 case "br":
10627                     pt.by = [w, h];
10628                 break;
10629                 case "tr":
10630                     pt.by = [w, -h];
10631                 break;
10632             }
10633
10634             arguments.callee.anim = this.fxanim(a,
10635                 o,
10636                 'motion',
10637                 .5,
10638                 "easeOut", after);
10639         });
10640         return this;
10641     },
10642
10643         /**
10644          * Ensures that all effects queued after syncFx is called on the element are
10645          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10646          * @return {Roo.Element} The Element
10647          */
10648     syncFx : function(){
10649         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10650             block : false,
10651             concurrent : true,
10652             stopFx : false
10653         });
10654         return this;
10655     },
10656
10657         /**
10658          * Ensures that all effects queued after sequenceFx is called on the element are
10659          * run in sequence.  This is the opposite of {@link #syncFx}.
10660          * @return {Roo.Element} The Element
10661          */
10662     sequenceFx : function(){
10663         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10664             block : false,
10665             concurrent : false,
10666             stopFx : false
10667         });
10668         return this;
10669     },
10670
10671         /* @private */
10672     nextFx : function(){
10673         var ef = this.fxQueue[0];
10674         if(ef){
10675             ef.call(this);
10676         }
10677     },
10678
10679         /**
10680          * Returns true if the element has any effects actively running or queued, else returns false.
10681          * @return {Boolean} True if element has active effects, else false
10682          */
10683     hasActiveFx : function(){
10684         return this.fxQueue && this.fxQueue[0];
10685     },
10686
10687         /**
10688          * Stops any running effects and clears the element's internal effects queue if it contains
10689          * any additional effects that haven't started yet.
10690          * @return {Roo.Element} The Element
10691          */
10692     stopFx : function(){
10693         if(this.hasActiveFx()){
10694             var cur = this.fxQueue[0];
10695             if(cur && cur.anim && cur.anim.isAnimated()){
10696                 this.fxQueue = [cur]; // clear out others
10697                 cur.anim.stop(true);
10698             }
10699         }
10700         return this;
10701     },
10702
10703         /* @private */
10704     beforeFx : function(o){
10705         if(this.hasActiveFx() && !o.concurrent){
10706            if(o.stopFx){
10707                this.stopFx();
10708                return true;
10709            }
10710            return false;
10711         }
10712         return true;
10713     },
10714
10715         /**
10716          * Returns true if the element is currently blocking so that no other effect can be queued
10717          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10718          * used to ensure that an effect initiated by a user action runs to completion prior to the
10719          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10720          * @return {Boolean} True if blocking, else false
10721          */
10722     hasFxBlock : function(){
10723         var q = this.fxQueue;
10724         return q && q[0] && q[0].block;
10725     },
10726
10727         /* @private */
10728     queueFx : function(o, fn){
10729         if(!this.fxQueue){
10730             this.fxQueue = [];
10731         }
10732         if(!this.hasFxBlock()){
10733             Roo.applyIf(o, this.fxDefaults);
10734             if(!o.concurrent){
10735                 var run = this.beforeFx(o);
10736                 fn.block = o.block;
10737                 this.fxQueue.push(fn);
10738                 if(run){
10739                     this.nextFx();
10740                 }
10741             }else{
10742                 fn.call(this);
10743             }
10744         }
10745         return this;
10746     },
10747
10748         /* @private */
10749     fxWrap : function(pos, o, vis){
10750         var wrap;
10751         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10752             var wrapXY;
10753             if(o.fixPosition){
10754                 wrapXY = this.getXY();
10755             }
10756             var div = document.createElement("div");
10757             div.style.visibility = vis;
10758             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10759             wrap.setPositioning(pos);
10760             if(wrap.getStyle("position") == "static"){
10761                 wrap.position("relative");
10762             }
10763             this.clearPositioning('auto');
10764             wrap.clip();
10765             wrap.dom.appendChild(this.dom);
10766             if(wrapXY){
10767                 wrap.setXY(wrapXY);
10768             }
10769         }
10770         return wrap;
10771     },
10772
10773         /* @private */
10774     fxUnwrap : function(wrap, pos, o){
10775         this.clearPositioning();
10776         this.setPositioning(pos);
10777         if(!o.wrap){
10778             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10779             wrap.remove();
10780         }
10781     },
10782
10783         /* @private */
10784     getFxRestore : function(){
10785         var st = this.dom.style;
10786         return {pos: this.getPositioning(), width: st.width, height : st.height};
10787     },
10788
10789         /* @private */
10790     afterFx : function(o){
10791         if(o.afterStyle){
10792             this.applyStyles(o.afterStyle);
10793         }
10794         if(o.afterCls){
10795             this.addClass(o.afterCls);
10796         }
10797         if(o.remove === true){
10798             this.remove();
10799         }
10800         Roo.callback(o.callback, o.scope, [this]);
10801         if(!o.concurrent){
10802             this.fxQueue.shift();
10803             this.nextFx();
10804         }
10805     },
10806
10807         /* @private */
10808     getFxEl : function(){ // support for composite element fx
10809         return Roo.get(this.dom);
10810     },
10811
10812         /* @private */
10813     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10814         animType = animType || 'run';
10815         opt = opt || {};
10816         var anim = Roo.lib.Anim[animType](
10817             this.dom, args,
10818             (opt.duration || defaultDur) || .35,
10819             (opt.easing || defaultEase) || 'easeOut',
10820             function(){
10821                 Roo.callback(cb, this);
10822             },
10823             this
10824         );
10825         opt.anim = anim;
10826         return anim;
10827     }
10828 };
10829
10830 // backwords compat
10831 Roo.Fx.resize = Roo.Fx.scale;
10832
10833 //When included, Roo.Fx is automatically applied to Element so that all basic
10834 //effects are available directly via the Element API
10835 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10836  * Based on:
10837  * Ext JS Library 1.1.1
10838  * Copyright(c) 2006-2007, Ext JS, LLC.
10839  *
10840  * Originally Released Under LGPL - original licence link has changed is not relivant.
10841  *
10842  * Fork - LGPL
10843  * <script type="text/javascript">
10844  */
10845
10846
10847 /**
10848  * @class Roo.CompositeElement
10849  * Standard composite class. Creates a Roo.Element for every element in the collection.
10850  * <br><br>
10851  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10852  * actions will be performed on all the elements in this collection.</b>
10853  * <br><br>
10854  * All methods return <i>this</i> and can be chained.
10855  <pre><code>
10856  var els = Roo.select("#some-el div.some-class", true);
10857  // or select directly from an existing element
10858  var el = Roo.get('some-el');
10859  el.select('div.some-class', true);
10860
10861  els.setWidth(100); // all elements become 100 width
10862  els.hide(true); // all elements fade out and hide
10863  // or
10864  els.setWidth(100).hide(true);
10865  </code></pre>
10866  */
10867 Roo.CompositeElement = function(els){
10868     this.elements = [];
10869     this.addElements(els);
10870 };
10871 Roo.CompositeElement.prototype = {
10872     isComposite: true,
10873     addElements : function(els){
10874         if(!els) return this;
10875         if(typeof els == "string"){
10876             els = Roo.Element.selectorFunction(els);
10877         }
10878         var yels = this.elements;
10879         var index = yels.length-1;
10880         for(var i = 0, len = els.length; i < len; i++) {
10881                 yels[++index] = Roo.get(els[i]);
10882         }
10883         return this;
10884     },
10885
10886     /**
10887     * Clears this composite and adds the elements returned by the passed selector.
10888     * @param {String/Array} els A string CSS selector, an array of elements or an element
10889     * @return {CompositeElement} this
10890     */
10891     fill : function(els){
10892         this.elements = [];
10893         this.add(els);
10894         return this;
10895     },
10896
10897     /**
10898     * Filters this composite to only elements that match the passed selector.
10899     * @param {String} selector A string CSS selector
10900     * @return {CompositeElement} this
10901     */
10902     filter : function(selector){
10903         var els = [];
10904         this.each(function(el){
10905             if(el.is(selector)){
10906                 els[els.length] = el.dom;
10907             }
10908         });
10909         this.fill(els);
10910         return this;
10911     },
10912
10913     invoke : function(fn, args){
10914         var els = this.elements;
10915         for(var i = 0, len = els.length; i < len; i++) {
10916                 Roo.Element.prototype[fn].apply(els[i], args);
10917         }
10918         return this;
10919     },
10920     /**
10921     * Adds elements to this composite.
10922     * @param {String/Array} els A string CSS selector, an array of elements or an element
10923     * @return {CompositeElement} this
10924     */
10925     add : function(els){
10926         if(typeof els == "string"){
10927             this.addElements(Roo.Element.selectorFunction(els));
10928         }else if(els.length !== undefined){
10929             this.addElements(els);
10930         }else{
10931             this.addElements([els]);
10932         }
10933         return this;
10934     },
10935     /**
10936     * Calls the passed function passing (el, this, index) for each element in this composite.
10937     * @param {Function} fn The function to call
10938     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10939     * @return {CompositeElement} this
10940     */
10941     each : function(fn, scope){
10942         var els = this.elements;
10943         for(var i = 0, len = els.length; i < len; i++){
10944             if(fn.call(scope || els[i], els[i], this, i) === false) {
10945                 break;
10946             }
10947         }
10948         return this;
10949     },
10950
10951     /**
10952      * Returns the Element object at the specified index
10953      * @param {Number} index
10954      * @return {Roo.Element}
10955      */
10956     item : function(index){
10957         return this.elements[index] || null;
10958     },
10959
10960     /**
10961      * Returns the first Element
10962      * @return {Roo.Element}
10963      */
10964     first : function(){
10965         return this.item(0);
10966     },
10967
10968     /**
10969      * Returns the last Element
10970      * @return {Roo.Element}
10971      */
10972     last : function(){
10973         return this.item(this.elements.length-1);
10974     },
10975
10976     /**
10977      * Returns the number of elements in this composite
10978      * @return Number
10979      */
10980     getCount : function(){
10981         return this.elements.length;
10982     },
10983
10984     /**
10985      * Returns true if this composite contains the passed element
10986      * @return Boolean
10987      */
10988     contains : function(el){
10989         return this.indexOf(el) !== -1;
10990     },
10991
10992     /**
10993      * Returns true if this composite contains the passed element
10994      * @return Boolean
10995      */
10996     indexOf : function(el){
10997         return this.elements.indexOf(Roo.get(el));
10998     },
10999
11000
11001     /**
11002     * Removes the specified element(s).
11003     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11004     * or an array of any of those.
11005     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11006     * @return {CompositeElement} this
11007     */
11008     removeElement : function(el, removeDom){
11009         if(el instanceof Array){
11010             for(var i = 0, len = el.length; i < len; i++){
11011                 this.removeElement(el[i]);
11012             }
11013             return this;
11014         }
11015         var index = typeof el == 'number' ? el : this.indexOf(el);
11016         if(index !== -1){
11017             if(removeDom){
11018                 var d = this.elements[index];
11019                 if(d.dom){
11020                     d.remove();
11021                 }else{
11022                     d.parentNode.removeChild(d);
11023                 }
11024             }
11025             this.elements.splice(index, 1);
11026         }
11027         return this;
11028     },
11029
11030     /**
11031     * Replaces the specified element with the passed element.
11032     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11033     * to replace.
11034     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11035     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11036     * @return {CompositeElement} this
11037     */
11038     replaceElement : function(el, replacement, domReplace){
11039         var index = typeof el == 'number' ? el : this.indexOf(el);
11040         if(index !== -1){
11041             if(domReplace){
11042                 this.elements[index].replaceWith(replacement);
11043             }else{
11044                 this.elements.splice(index, 1, Roo.get(replacement))
11045             }
11046         }
11047         return this;
11048     },
11049
11050     /**
11051      * Removes all elements.
11052      */
11053     clear : function(){
11054         this.elements = [];
11055     }
11056 };
11057 (function(){
11058     Roo.CompositeElement.createCall = function(proto, fnName){
11059         if(!proto[fnName]){
11060             proto[fnName] = function(){
11061                 return this.invoke(fnName, arguments);
11062             };
11063         }
11064     };
11065     for(var fnName in Roo.Element.prototype){
11066         if(typeof Roo.Element.prototype[fnName] == "function"){
11067             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11068         }
11069     };
11070 })();
11071 /*
11072  * Based on:
11073  * Ext JS Library 1.1.1
11074  * Copyright(c) 2006-2007, Ext JS, LLC.
11075  *
11076  * Originally Released Under LGPL - original licence link has changed is not relivant.
11077  *
11078  * Fork - LGPL
11079  * <script type="text/javascript">
11080  */
11081
11082 /**
11083  * @class Roo.CompositeElementLite
11084  * @extends Roo.CompositeElement
11085  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11086  <pre><code>
11087  var els = Roo.select("#some-el div.some-class");
11088  // or select directly from an existing element
11089  var el = Roo.get('some-el');
11090  el.select('div.some-class');
11091
11092  els.setWidth(100); // all elements become 100 width
11093  els.hide(true); // all elements fade out and hide
11094  // or
11095  els.setWidth(100).hide(true);
11096  </code></pre><br><br>
11097  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11098  * actions will be performed on all the elements in this collection.</b>
11099  */
11100 Roo.CompositeElementLite = function(els){
11101     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11102     this.el = new Roo.Element.Flyweight();
11103 };
11104 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11105     addElements : function(els){
11106         if(els){
11107             if(els instanceof Array){
11108                 this.elements = this.elements.concat(els);
11109             }else{
11110                 var yels = this.elements;
11111                 var index = yels.length-1;
11112                 for(var i = 0, len = els.length; i < len; i++) {
11113                     yels[++index] = els[i];
11114                 }
11115             }
11116         }
11117         return this;
11118     },
11119     invoke : function(fn, args){
11120         var els = this.elements;
11121         var el = this.el;
11122         for(var i = 0, len = els.length; i < len; i++) {
11123             el.dom = els[i];
11124                 Roo.Element.prototype[fn].apply(el, args);
11125         }
11126         return this;
11127     },
11128     /**
11129      * Returns a flyweight Element of the dom element object at the specified index
11130      * @param {Number} index
11131      * @return {Roo.Element}
11132      */
11133     item : function(index){
11134         if(!this.elements[index]){
11135             return null;
11136         }
11137         this.el.dom = this.elements[index];
11138         return this.el;
11139     },
11140
11141     // fixes scope with flyweight
11142     addListener : function(eventName, handler, scope, opt){
11143         var els = this.elements;
11144         for(var i = 0, len = els.length; i < len; i++) {
11145             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11146         }
11147         return this;
11148     },
11149
11150     /**
11151     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11152     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11153     * a reference to the dom node, use el.dom.</b>
11154     * @param {Function} fn The function to call
11155     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11156     * @return {CompositeElement} this
11157     */
11158     each : function(fn, scope){
11159         var els = this.elements;
11160         var el = this.el;
11161         for(var i = 0, len = els.length; i < len; i++){
11162             el.dom = els[i];
11163                 if(fn.call(scope || el, el, this, i) === false){
11164                 break;
11165             }
11166         }
11167         return this;
11168     },
11169
11170     indexOf : function(el){
11171         return this.elements.indexOf(Roo.getDom(el));
11172     },
11173
11174     replaceElement : function(el, replacement, domReplace){
11175         var index = typeof el == 'number' ? el : this.indexOf(el);
11176         if(index !== -1){
11177             replacement = Roo.getDom(replacement);
11178             if(domReplace){
11179                 var d = this.elements[index];
11180                 d.parentNode.insertBefore(replacement, d);
11181                 d.parentNode.removeChild(d);
11182             }
11183             this.elements.splice(index, 1, replacement);
11184         }
11185         return this;
11186     }
11187 });
11188 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11189
11190 /*
11191  * Based on:
11192  * Ext JS Library 1.1.1
11193  * Copyright(c) 2006-2007, Ext JS, LLC.
11194  *
11195  * Originally Released Under LGPL - original licence link has changed is not relivant.
11196  *
11197  * Fork - LGPL
11198  * <script type="text/javascript">
11199  */
11200
11201  
11202
11203 /**
11204  * @class Roo.data.Connection
11205  * @extends Roo.util.Observable
11206  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11207  * either to a configured URL, or to a URL specified at request time.<br><br>
11208  * <p>
11209  * Requests made by this class are asynchronous, and will return immediately. No data from
11210  * the server will be available to the statement immediately following the {@link #request} call.
11211  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11212  * <p>
11213  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11214  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11215  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11216  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11217  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11218  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11219  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11220  * standard DOM methods.
11221  * @constructor
11222  * @param {Object} config a configuration object.
11223  */
11224 Roo.data.Connection = function(config){
11225     Roo.apply(this, config);
11226     this.addEvents({
11227         /**
11228          * @event beforerequest
11229          * Fires before a network request is made to retrieve a data object.
11230          * @param {Connection} conn This Connection object.
11231          * @param {Object} options The options config object passed to the {@link #request} method.
11232          */
11233         "beforerequest" : true,
11234         /**
11235          * @event requestcomplete
11236          * Fires if the request was successfully completed.
11237          * @param {Connection} conn This Connection object.
11238          * @param {Object} response The XHR object containing the response data.
11239          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11240          * @param {Object} options The options config object passed to the {@link #request} method.
11241          */
11242         "requestcomplete" : true,
11243         /**
11244          * @event requestexception
11245          * Fires if an error HTTP status was returned from the server.
11246          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11247          * @param {Connection} conn This Connection object.
11248          * @param {Object} response The XHR object containing the response data.
11249          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11250          * @param {Object} options The options config object passed to the {@link #request} method.
11251          */
11252         "requestexception" : true
11253     });
11254     Roo.data.Connection.superclass.constructor.call(this);
11255 };
11256
11257 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11258     /**
11259      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11260      */
11261     /**
11262      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11263      * extra parameters to each request made by this object. (defaults to undefined)
11264      */
11265     /**
11266      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11267      *  to each request made by this object. (defaults to undefined)
11268      */
11269     /**
11270      * @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)
11271      */
11272     /**
11273      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11274      */
11275     timeout : 30000,
11276     /**
11277      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11278      * @type Boolean
11279      */
11280     autoAbort:false,
11281
11282     /**
11283      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11284      * @type Boolean
11285      */
11286     disableCaching: true,
11287
11288     /**
11289      * Sends an HTTP request to a remote server.
11290      * @param {Object} options An object which may contain the following properties:<ul>
11291      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11292      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11293      * request, a url encoded string or a function to call to get either.</li>
11294      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11295      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11296      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11297      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11298      * <li>options {Object} The parameter to the request call.</li>
11299      * <li>success {Boolean} True if the request succeeded.</li>
11300      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11301      * </ul></li>
11302      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11303      * The callback is passed the following parameters:<ul>
11304      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11305      * <li>options {Object} The parameter to the request call.</li>
11306      * </ul></li>
11307      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11308      * The callback is passed the following parameters:<ul>
11309      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11310      * <li>options {Object} The parameter to the request call.</li>
11311      * </ul></li>
11312      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11313      * for the callback function. Defaults to the browser window.</li>
11314      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11315      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11316      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11317      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11318      * params for the post data. Any params will be appended to the URL.</li>
11319      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11320      * </ul>
11321      * @return {Number} transactionId
11322      */
11323     request : function(o){
11324         if(this.fireEvent("beforerequest", this, o) !== false){
11325             var p = o.params;
11326
11327             if(typeof p == "function"){
11328                 p = p.call(o.scope||window, o);
11329             }
11330             if(typeof p == "object"){
11331                 p = Roo.urlEncode(o.params);
11332             }
11333             if(this.extraParams){
11334                 var extras = Roo.urlEncode(this.extraParams);
11335                 p = p ? (p + '&' + extras) : extras;
11336             }
11337
11338             var url = o.url || this.url;
11339             if(typeof url == 'function'){
11340                 url = url.call(o.scope||window, o);
11341             }
11342
11343             if(o.form){
11344                 var form = Roo.getDom(o.form);
11345                 url = url || form.action;
11346
11347                 var enctype = form.getAttribute("enctype");
11348                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11349                     return this.doFormUpload(o, p, url);
11350                 }
11351                 var f = Roo.lib.Ajax.serializeForm(form);
11352                 p = p ? (p + '&' + f) : f;
11353             }
11354
11355             var hs = o.headers;
11356             if(this.defaultHeaders){
11357                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11358                 if(!o.headers){
11359                     o.headers = hs;
11360                 }
11361             }
11362
11363             var cb = {
11364                 success: this.handleResponse,
11365                 failure: this.handleFailure,
11366                 scope: this,
11367                 argument: {options: o},
11368                 timeout : this.timeout
11369             };
11370
11371             var method = o.method||this.method||(p ? "POST" : "GET");
11372
11373             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11374                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11375             }
11376
11377             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11378                 if(o.autoAbort){
11379                     this.abort();
11380                 }
11381             }else if(this.autoAbort !== false){
11382                 this.abort();
11383             }
11384
11385             if((method == 'GET' && p) || o.xmlData){
11386                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11387                 p = '';
11388             }
11389             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11390             return this.transId;
11391         }else{
11392             Roo.callback(o.callback, o.scope, [o, null, null]);
11393             return null;
11394         }
11395     },
11396
11397     /**
11398      * Determine whether this object has a request outstanding.
11399      * @param {Number} transactionId (Optional) defaults to the last transaction
11400      * @return {Boolean} True if there is an outstanding request.
11401      */
11402     isLoading : function(transId){
11403         if(transId){
11404             return Roo.lib.Ajax.isCallInProgress(transId);
11405         }else{
11406             return this.transId ? true : false;
11407         }
11408     },
11409
11410     /**
11411      * Aborts any outstanding request.
11412      * @param {Number} transactionId (Optional) defaults to the last transaction
11413      */
11414     abort : function(transId){
11415         if(transId || this.isLoading()){
11416             Roo.lib.Ajax.abort(transId || this.transId);
11417         }
11418     },
11419
11420     // private
11421     handleResponse : function(response){
11422         this.transId = false;
11423         var options = response.argument.options;
11424         response.argument = options ? options.argument : null;
11425         this.fireEvent("requestcomplete", this, response, options);
11426         Roo.callback(options.success, options.scope, [response, options]);
11427         Roo.callback(options.callback, options.scope, [options, true, response]);
11428     },
11429
11430     // private
11431     handleFailure : function(response, e){
11432         this.transId = false;
11433         var options = response.argument.options;
11434         response.argument = options ? options.argument : null;
11435         this.fireEvent("requestexception", this, response, options, e);
11436         Roo.callback(options.failure, options.scope, [response, options]);
11437         Roo.callback(options.callback, options.scope, [options, false, response]);
11438     },
11439
11440     // private
11441     doFormUpload : function(o, ps, url){
11442         var id = Roo.id();
11443         var frame = document.createElement('iframe');
11444         frame.id = id;
11445         frame.name = id;
11446         frame.className = 'x-hidden';
11447         if(Roo.isIE){
11448             frame.src = Roo.SSL_SECURE_URL;
11449         }
11450         document.body.appendChild(frame);
11451
11452         if(Roo.isIE){
11453            document.frames[id].name = id;
11454         }
11455
11456         var form = Roo.getDom(o.form);
11457         form.target = id;
11458         form.method = 'POST';
11459         form.enctype = form.encoding = 'multipart/form-data';
11460         if(url){
11461             form.action = url;
11462         }
11463
11464         var hiddens, hd;
11465         if(ps){ // add dynamic params
11466             hiddens = [];
11467             ps = Roo.urlDecode(ps, false);
11468             for(var k in ps){
11469                 if(ps.hasOwnProperty(k)){
11470                     hd = document.createElement('input');
11471                     hd.type = 'hidden';
11472                     hd.name = k;
11473                     hd.value = ps[k];
11474                     form.appendChild(hd);
11475                     hiddens.push(hd);
11476                 }
11477             }
11478         }
11479
11480         function cb(){
11481             var r = {  // bogus response object
11482                 responseText : '',
11483                 responseXML : null
11484             };
11485
11486             r.argument = o ? o.argument : null;
11487
11488             try { //
11489                 var doc;
11490                 if(Roo.isIE){
11491                     doc = frame.contentWindow.document;
11492                 }else {
11493                     doc = (frame.contentDocument || window.frames[id].document);
11494                 }
11495                 if(doc && doc.body){
11496                     r.responseText = doc.body.innerHTML;
11497                 }
11498                 if(doc && doc.XMLDocument){
11499                     r.responseXML = doc.XMLDocument;
11500                 }else {
11501                     r.responseXML = doc;
11502                 }
11503             }
11504             catch(e) {
11505                 // ignore
11506             }
11507
11508             Roo.EventManager.removeListener(frame, 'load', cb, this);
11509
11510             this.fireEvent("requestcomplete", this, r, o);
11511             Roo.callback(o.success, o.scope, [r, o]);
11512             Roo.callback(o.callback, o.scope, [o, true, r]);
11513
11514             setTimeout(function(){document.body.removeChild(frame);}, 100);
11515         }
11516
11517         Roo.EventManager.on(frame, 'load', cb, this);
11518         form.submit();
11519
11520         if(hiddens){ // remove dynamic params
11521             for(var i = 0, len = hiddens.length; i < len; i++){
11522                 form.removeChild(hiddens[i]);
11523             }
11524         }
11525     }
11526 });
11527
11528 /**
11529  * @class Roo.Ajax
11530  * @extends Roo.data.Connection
11531  * Global Ajax request class.
11532  *
11533  * @singleton
11534  */
11535 Roo.Ajax = new Roo.data.Connection({
11536     // fix up the docs
11537    /**
11538      * @cfg {String} url @hide
11539      */
11540     /**
11541      * @cfg {Object} extraParams @hide
11542      */
11543     /**
11544      * @cfg {Object} defaultHeaders @hide
11545      */
11546     /**
11547      * @cfg {String} method (Optional) @hide
11548      */
11549     /**
11550      * @cfg {Number} timeout (Optional) @hide
11551      */
11552     /**
11553      * @cfg {Boolean} autoAbort (Optional) @hide
11554      */
11555
11556     /**
11557      * @cfg {Boolean} disableCaching (Optional) @hide
11558      */
11559
11560     /**
11561      * @property  disableCaching
11562      * True to add a unique cache-buster param to GET requests. (defaults to true)
11563      * @type Boolean
11564      */
11565     /**
11566      * @property  url
11567      * The default URL to be used for requests to the server. (defaults to undefined)
11568      * @type String
11569      */
11570     /**
11571      * @property  extraParams
11572      * An object containing properties which are used as
11573      * extra parameters to each request made by this object. (defaults to undefined)
11574      * @type Object
11575      */
11576     /**
11577      * @property  defaultHeaders
11578      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11579      * @type Object
11580      */
11581     /**
11582      * @property  method
11583      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11584      * @type String
11585      */
11586     /**
11587      * @property  timeout
11588      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11589      * @type Number
11590      */
11591
11592     /**
11593      * @property  autoAbort
11594      * Whether a new request should abort any pending requests. (defaults to false)
11595      * @type Boolean
11596      */
11597     autoAbort : false,
11598
11599     /**
11600      * Serialize the passed form into a url encoded string
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  * Global Ajax request class.
11620  * 
11621  * @class Roo.Ajax
11622  * @extends Roo.data.Connection
11623  * @static
11624  * 
11625  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11626  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11627  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11628  * @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)
11629  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11630  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11631  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11632  */
11633 Roo.Ajax = new Roo.data.Connection({
11634     // fix up the docs
11635     /**
11636      * @scope Roo.Ajax
11637      * @type {Boolear} 
11638      */
11639     autoAbort : false,
11640
11641     /**
11642      * Serialize the passed form into a url encoded string
11643      * @scope Roo.Ajax
11644      * @param {String/HTMLElement} form
11645      * @return {String}
11646      */
11647     serializeForm : function(form){
11648         return Roo.lib.Ajax.serializeForm(form);
11649     }
11650 });/*
11651  * Based on:
11652  * Ext JS Library 1.1.1
11653  * Copyright(c) 2006-2007, Ext JS, LLC.
11654  *
11655  * Originally Released Under LGPL - original licence link has changed is not relivant.
11656  *
11657  * Fork - LGPL
11658  * <script type="text/javascript">
11659  */
11660
11661  
11662 /**
11663  * @class Roo.UpdateManager
11664  * @extends Roo.util.Observable
11665  * Provides AJAX-style update for Element object.<br><br>
11666  * Usage:<br>
11667  * <pre><code>
11668  * // Get it from a Roo.Element object
11669  * var el = Roo.get("foo");
11670  * var mgr = el.getUpdateManager();
11671  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11672  * ...
11673  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11674  * <br>
11675  * // or directly (returns the same UpdateManager instance)
11676  * var mgr = new Roo.UpdateManager("myElementId");
11677  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11678  * mgr.on("update", myFcnNeedsToKnow);
11679  * <br>
11680    // short handed call directly from the element object
11681    Roo.get("foo").load({
11682         url: "bar.php",
11683         scripts:true,
11684         params: "for=bar",
11685         text: "Loading Foo..."
11686    });
11687  * </code></pre>
11688  * @constructor
11689  * Create new UpdateManager directly.
11690  * @param {String/HTMLElement/Roo.Element} el The element to update
11691  * @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).
11692  */
11693 Roo.UpdateManager = function(el, forceNew){
11694     el = Roo.get(el);
11695     if(!forceNew && el.updateManager){
11696         return el.updateManager;
11697     }
11698     /**
11699      * The Element object
11700      * @type Roo.Element
11701      */
11702     this.el = el;
11703     /**
11704      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11705      * @type String
11706      */
11707     this.defaultUrl = null;
11708
11709     this.addEvents({
11710         /**
11711          * @event beforeupdate
11712          * Fired before an update is made, return false from your handler and the update is cancelled.
11713          * @param {Roo.Element} el
11714          * @param {String/Object/Function} url
11715          * @param {String/Object} params
11716          */
11717         "beforeupdate": true,
11718         /**
11719          * @event update
11720          * Fired after successful update is made.
11721          * @param {Roo.Element} el
11722          * @param {Object} oResponseObject The response Object
11723          */
11724         "update": true,
11725         /**
11726          * @event failure
11727          * Fired on update failure.
11728          * @param {Roo.Element} el
11729          * @param {Object} oResponseObject The response Object
11730          */
11731         "failure": true
11732     });
11733     var d = Roo.UpdateManager.defaults;
11734     /**
11735      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11736      * @type String
11737      */
11738     this.sslBlankUrl = d.sslBlankUrl;
11739     /**
11740      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11741      * @type Boolean
11742      */
11743     this.disableCaching = d.disableCaching;
11744     /**
11745      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11746      * @type String
11747      */
11748     this.indicatorText = d.indicatorText;
11749     /**
11750      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11751      * @type String
11752      */
11753     this.showLoadIndicator = d.showLoadIndicator;
11754     /**
11755      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11756      * @type Number
11757      */
11758     this.timeout = d.timeout;
11759
11760     /**
11761      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11762      * @type Boolean
11763      */
11764     this.loadScripts = d.loadScripts;
11765
11766     /**
11767      * Transaction object of current executing transaction
11768      */
11769     this.transaction = null;
11770
11771     /**
11772      * @private
11773      */
11774     this.autoRefreshProcId = null;
11775     /**
11776      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11777      * @type Function
11778      */
11779     this.refreshDelegate = this.refresh.createDelegate(this);
11780     /**
11781      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11782      * @type Function
11783      */
11784     this.updateDelegate = this.update.createDelegate(this);
11785     /**
11786      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11787      * @type Function
11788      */
11789     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11790     /**
11791      * @private
11792      */
11793     this.successDelegate = this.processSuccess.createDelegate(this);
11794     /**
11795      * @private
11796      */
11797     this.failureDelegate = this.processFailure.createDelegate(this);
11798
11799     if(!this.renderer){
11800      /**
11801       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11802       */
11803     this.renderer = new Roo.UpdateManager.BasicRenderer();
11804     }
11805     
11806     Roo.UpdateManager.superclass.constructor.call(this);
11807 };
11808
11809 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11810     /**
11811      * Get the Element this UpdateManager is bound to
11812      * @return {Roo.Element} The element
11813      */
11814     getEl : function(){
11815         return this.el;
11816     },
11817     /**
11818      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11819      * @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:
11820 <pre><code>
11821 um.update({<br/>
11822     url: "your-url.php",<br/>
11823     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11824     callback: yourFunction,<br/>
11825     scope: yourObject, //(optional scope)  <br/>
11826     discardUrl: false, <br/>
11827     nocache: false,<br/>
11828     text: "Loading...",<br/>
11829     timeout: 30,<br/>
11830     scripts: false<br/>
11831 });
11832 </code></pre>
11833      * The only required property is url. The optional properties nocache, text and scripts
11834      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11835      * @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}
11836      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11837      * @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.
11838      */
11839     update : function(url, params, callback, discardUrl){
11840         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11841             var method = this.method, cfg;
11842             if(typeof url == "object"){ // must be config object
11843                 cfg = url;
11844                 url = cfg.url;
11845                 params = params || cfg.params;
11846                 callback = callback || cfg.callback;
11847                 discardUrl = discardUrl || cfg.discardUrl;
11848                 if(callback && cfg.scope){
11849                     callback = callback.createDelegate(cfg.scope);
11850                 }
11851                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11852                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11853                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11854                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11855                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11856             }
11857             this.showLoading();
11858             if(!discardUrl){
11859                 this.defaultUrl = url;
11860             }
11861             if(typeof url == "function"){
11862                 url = url.call(this);
11863             }
11864
11865             method = method || (params ? "POST" : "GET");
11866             if(method == "GET"){
11867                 url = this.prepareUrl(url);
11868             }
11869
11870             var o = Roo.apply(cfg ||{}, {
11871                 url : url,
11872                 params: params,
11873                 success: this.successDelegate,
11874                 failure: this.failureDelegate,
11875                 callback: undefined,
11876                 timeout: (this.timeout*1000),
11877                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11878             });
11879
11880             this.transaction = Roo.Ajax.request(o);
11881         }
11882     },
11883
11884     /**
11885      * 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.
11886      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11887      * @param {String/HTMLElement} form The form Id or form element
11888      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11889      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11890      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11891      */
11892     formUpdate : function(form, url, reset, callback){
11893         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11894             if(typeof url == "function"){
11895                 url = url.call(this);
11896             }
11897             form = Roo.getDom(form);
11898             this.transaction = Roo.Ajax.request({
11899                 form: form,
11900                 url:url,
11901                 success: this.successDelegate,
11902                 failure: this.failureDelegate,
11903                 timeout: (this.timeout*1000),
11904                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11905             });
11906             this.showLoading.defer(1, this);
11907         }
11908     },
11909
11910     /**
11911      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11912      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11913      */
11914     refresh : function(callback){
11915         if(this.defaultUrl == null){
11916             return;
11917         }
11918         this.update(this.defaultUrl, null, callback, true);
11919     },
11920
11921     /**
11922      * Set this element to auto refresh.
11923      * @param {Number} interval How often to update (in seconds).
11924      * @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)
11925      * @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}
11926      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11927      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11928      */
11929     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11930         if(refreshNow){
11931             this.update(url || this.defaultUrl, params, callback, true);
11932         }
11933         if(this.autoRefreshProcId){
11934             clearInterval(this.autoRefreshProcId);
11935         }
11936         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11937     },
11938
11939     /**
11940      * Stop auto refresh on this element.
11941      */
11942      stopAutoRefresh : function(){
11943         if(this.autoRefreshProcId){
11944             clearInterval(this.autoRefreshProcId);
11945             delete this.autoRefreshProcId;
11946         }
11947     },
11948
11949     isAutoRefreshing : function(){
11950        return this.autoRefreshProcId ? true : false;
11951     },
11952     /**
11953      * Called to update the element to "Loading" state. Override to perform custom action.
11954      */
11955     showLoading : function(){
11956         if(this.showLoadIndicator){
11957             this.el.update(this.indicatorText);
11958         }
11959     },
11960
11961     /**
11962      * Adds unique parameter to query string if disableCaching = true
11963      * @private
11964      */
11965     prepareUrl : function(url){
11966         if(this.disableCaching){
11967             var append = "_dc=" + (new Date().getTime());
11968             if(url.indexOf("?") !== -1){
11969                 url += "&" + append;
11970             }else{
11971                 url += "?" + append;
11972             }
11973         }
11974         return url;
11975     },
11976
11977     /**
11978      * @private
11979      */
11980     processSuccess : function(response){
11981         this.transaction = null;
11982         if(response.argument.form && response.argument.reset){
11983             try{ // put in try/catch since some older FF releases had problems with this
11984                 response.argument.form.reset();
11985             }catch(e){}
11986         }
11987         if(this.loadScripts){
11988             this.renderer.render(this.el, response, this,
11989                 this.updateComplete.createDelegate(this, [response]));
11990         }else{
11991             this.renderer.render(this.el, response, this);
11992             this.updateComplete(response);
11993         }
11994     },
11995
11996     updateComplete : function(response){
11997         this.fireEvent("update", this.el, response);
11998         if(typeof response.argument.callback == "function"){
11999             response.argument.callback(this.el, true, response);
12000         }
12001     },
12002
12003     /**
12004      * @private
12005      */
12006     processFailure : function(response){
12007         this.transaction = null;
12008         this.fireEvent("failure", this.el, response);
12009         if(typeof response.argument.callback == "function"){
12010             response.argument.callback(this.el, false, response);
12011         }
12012     },
12013
12014     /**
12015      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12016      * @param {Object} renderer The object implementing the render() method
12017      */
12018     setRenderer : function(renderer){
12019         this.renderer = renderer;
12020     },
12021
12022     getRenderer : function(){
12023        return this.renderer;
12024     },
12025
12026     /**
12027      * Set the defaultUrl used for updates
12028      * @param {String/Function} defaultUrl The url or a function to call to get the url
12029      */
12030     setDefaultUrl : function(defaultUrl){
12031         this.defaultUrl = defaultUrl;
12032     },
12033
12034     /**
12035      * Aborts the executing transaction
12036      */
12037     abort : function(){
12038         if(this.transaction){
12039             Roo.Ajax.abort(this.transaction);
12040         }
12041     },
12042
12043     /**
12044      * Returns true if an update is in progress
12045      * @return {Boolean}
12046      */
12047     isUpdating : function(){
12048         if(this.transaction){
12049             return Roo.Ajax.isLoading(this.transaction);
12050         }
12051         return false;
12052     }
12053 });
12054
12055 /**
12056  * @class Roo.UpdateManager.defaults
12057  * @static (not really - but it helps the doc tool)
12058  * The defaults collection enables customizing the default properties of UpdateManager
12059  */
12060    Roo.UpdateManager.defaults = {
12061        /**
12062          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12063          * @type Number
12064          */
12065          timeout : 30,
12066
12067          /**
12068          * True to process scripts by default (Defaults to false).
12069          * @type Boolean
12070          */
12071         loadScripts : false,
12072
12073         /**
12074         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12075         * @type String
12076         */
12077         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12078         /**
12079          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12080          * @type Boolean
12081          */
12082         disableCaching : false,
12083         /**
12084          * Whether to show indicatorText when loading (Defaults to true).
12085          * @type Boolean
12086          */
12087         showLoadIndicator : true,
12088         /**
12089          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12090          * @type String
12091          */
12092         indicatorText : '<div class="loading-indicator">Loading...</div>'
12093    };
12094
12095 /**
12096  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12097  *Usage:
12098  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12099  * @param {String/HTMLElement/Roo.Element} el The element to update
12100  * @param {String} url The url
12101  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12102  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12103  * @static
12104  * @deprecated
12105  * @member Roo.UpdateManager
12106  */
12107 Roo.UpdateManager.updateElement = function(el, url, params, options){
12108     var um = Roo.get(el, true).getUpdateManager();
12109     Roo.apply(um, options);
12110     um.update(url, params, options ? options.callback : null);
12111 };
12112 // alias for backwards compat
12113 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12114 /**
12115  * @class Roo.UpdateManager.BasicRenderer
12116  * Default Content renderer. Updates the elements innerHTML with the responseText.
12117  */
12118 Roo.UpdateManager.BasicRenderer = function(){};
12119
12120 Roo.UpdateManager.BasicRenderer.prototype = {
12121     /**
12122      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12123      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12124      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12125      * @param {Roo.Element} el The element being rendered
12126      * @param {Object} response The YUI Connect response object
12127      * @param {UpdateManager} updateManager The calling update manager
12128      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12129      */
12130      render : function(el, response, updateManager, callback){
12131         el.update(response.responseText, updateManager.loadScripts, callback);
12132     }
12133 };
12134 /*
12135  * Based on:
12136  * Ext JS Library 1.1.1
12137  * Copyright(c) 2006-2007, Ext JS, LLC.
12138  *
12139  * Originally Released Under LGPL - original licence link has changed is not relivant.
12140  *
12141  * Fork - LGPL
12142  * <script type="text/javascript">
12143  */
12144
12145 /**
12146  * @class Roo.util.DelayedTask
12147  * Provides a convenient method of performing setTimeout where a new
12148  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12149  * You can use this class to buffer
12150  * the keypress events for a certain number of milliseconds, and perform only if they stop
12151  * for that amount of time.
12152  * @constructor The parameters to this constructor serve as defaults and are not required.
12153  * @param {Function} fn (optional) The default function to timeout
12154  * @param {Object} scope (optional) The default scope of that timeout
12155  * @param {Array} args (optional) The default Array of arguments
12156  */
12157 Roo.util.DelayedTask = function(fn, scope, args){
12158     var id = null, d, t;
12159
12160     var call = function(){
12161         var now = new Date().getTime();
12162         if(now - t >= d){
12163             clearInterval(id);
12164             id = null;
12165             fn.apply(scope, args || []);
12166         }
12167     };
12168     /**
12169      * Cancels any pending timeout and queues a new one
12170      * @param {Number} delay The milliseconds to delay
12171      * @param {Function} newFn (optional) Overrides function passed to constructor
12172      * @param {Object} newScope (optional) Overrides scope passed to constructor
12173      * @param {Array} newArgs (optional) Overrides args passed to constructor
12174      */
12175     this.delay = function(delay, newFn, newScope, newArgs){
12176         if(id && delay != d){
12177             this.cancel();
12178         }
12179         d = delay;
12180         t = new Date().getTime();
12181         fn = newFn || fn;
12182         scope = newScope || scope;
12183         args = newArgs || args;
12184         if(!id){
12185             id = setInterval(call, d);
12186         }
12187     };
12188
12189     /**
12190      * Cancel the last queued timeout
12191      */
12192     this.cancel = function(){
12193         if(id){
12194             clearInterval(id);
12195             id = null;
12196         }
12197     };
12198 };/*
12199  * Based on:
12200  * Ext JS Library 1.1.1
12201  * Copyright(c) 2006-2007, Ext JS, LLC.
12202  *
12203  * Originally Released Under LGPL - original licence link has changed is not relivant.
12204  *
12205  * Fork - LGPL
12206  * <script type="text/javascript">
12207  */
12208  
12209  
12210 Roo.util.TaskRunner = function(interval){
12211     interval = interval || 10;
12212     var tasks = [], removeQueue = [];
12213     var id = 0;
12214     var running = false;
12215
12216     var stopThread = function(){
12217         running = false;
12218         clearInterval(id);
12219         id = 0;
12220     };
12221
12222     var startThread = function(){
12223         if(!running){
12224             running = true;
12225             id = setInterval(runTasks, interval);
12226         }
12227     };
12228
12229     var removeTask = function(task){
12230         removeQueue.push(task);
12231         if(task.onStop){
12232             task.onStop();
12233         }
12234     };
12235
12236     var runTasks = function(){
12237         if(removeQueue.length > 0){
12238             for(var i = 0, len = removeQueue.length; i < len; i++){
12239                 tasks.remove(removeQueue[i]);
12240             }
12241             removeQueue = [];
12242             if(tasks.length < 1){
12243                 stopThread();
12244                 return;
12245             }
12246         }
12247         var now = new Date().getTime();
12248         for(var i = 0, len = tasks.length; i < len; ++i){
12249             var t = tasks[i];
12250             var itime = now - t.taskRunTime;
12251             if(t.interval <= itime){
12252                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12253                 t.taskRunTime = now;
12254                 if(rt === false || t.taskRunCount === t.repeat){
12255                     removeTask(t);
12256                     return;
12257                 }
12258             }
12259             if(t.duration && t.duration <= (now - t.taskStartTime)){
12260                 removeTask(t);
12261             }
12262         }
12263     };
12264
12265     /**
12266      * Queues a new task.
12267      * @param {Object} task
12268      */
12269     this.start = function(task){
12270         tasks.push(task);
12271         task.taskStartTime = new Date().getTime();
12272         task.taskRunTime = 0;
12273         task.taskRunCount = 0;
12274         startThread();
12275         return task;
12276     };
12277
12278     this.stop = function(task){
12279         removeTask(task);
12280         return task;
12281     };
12282
12283     this.stopAll = function(){
12284         stopThread();
12285         for(var i = 0, len = tasks.length; i < len; i++){
12286             if(tasks[i].onStop){
12287                 tasks[i].onStop();
12288             }
12289         }
12290         tasks = [];
12291         removeQueue = [];
12292     };
12293 };
12294
12295 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12296  * Based on:
12297  * Ext JS Library 1.1.1
12298  * Copyright(c) 2006-2007, Ext JS, LLC.
12299  *
12300  * Originally Released Under LGPL - original licence link has changed is not relivant.
12301  *
12302  * Fork - LGPL
12303  * <script type="text/javascript">
12304  */
12305
12306  
12307 /**
12308  * @class Roo.util.MixedCollection
12309  * @extends Roo.util.Observable
12310  * A Collection class that maintains both numeric indexes and keys and exposes events.
12311  * @constructor
12312  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12313  * collection (defaults to false)
12314  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12315  * and return the key value for that item.  This is used when available to look up the key on items that
12316  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12317  * equivalent to providing an implementation for the {@link #getKey} method.
12318  */
12319 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12320     this.items = [];
12321     this.map = {};
12322     this.keys = [];
12323     this.length = 0;
12324     this.addEvents({
12325         /**
12326          * @event clear
12327          * Fires when the collection is cleared.
12328          */
12329         "clear" : true,
12330         /**
12331          * @event add
12332          * Fires when an item is added to the collection.
12333          * @param {Number} index The index at which the item was added.
12334          * @param {Object} o The item added.
12335          * @param {String} key The key associated with the added item.
12336          */
12337         "add" : true,
12338         /**
12339          * @event replace
12340          * Fires when an item is replaced in the collection.
12341          * @param {String} key he key associated with the new added.
12342          * @param {Object} old The item being replaced.
12343          * @param {Object} new The new item.
12344          */
12345         "replace" : true,
12346         /**
12347          * @event remove
12348          * Fires when an item is removed from the collection.
12349          * @param {Object} o The item being removed.
12350          * @param {String} key (optional) The key associated with the removed item.
12351          */
12352         "remove" : true,
12353         "sort" : true
12354     });
12355     this.allowFunctions = allowFunctions === true;
12356     if(keyFn){
12357         this.getKey = keyFn;
12358     }
12359     Roo.util.MixedCollection.superclass.constructor.call(this);
12360 };
12361
12362 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12363     allowFunctions : false,
12364     
12365 /**
12366  * Adds an item to the collection.
12367  * @param {String} key The key to associate with the item
12368  * @param {Object} o The item to add.
12369  * @return {Object} The item added.
12370  */
12371     add : function(key, o){
12372         if(arguments.length == 1){
12373             o = arguments[0];
12374             key = this.getKey(o);
12375         }
12376         if(typeof key == "undefined" || key === null){
12377             this.length++;
12378             this.items.push(o);
12379             this.keys.push(null);
12380         }else{
12381             var old = this.map[key];
12382             if(old){
12383                 return this.replace(key, o);
12384             }
12385             this.length++;
12386             this.items.push(o);
12387             this.map[key] = o;
12388             this.keys.push(key);
12389         }
12390         this.fireEvent("add", this.length-1, o, key);
12391         return o;
12392     },
12393        
12394 /**
12395   * MixedCollection has a generic way to fetch keys if you implement getKey.
12396 <pre><code>
12397 // normal way
12398 var mc = new Roo.util.MixedCollection();
12399 mc.add(someEl.dom.id, someEl);
12400 mc.add(otherEl.dom.id, otherEl);
12401 //and so on
12402
12403 // using getKey
12404 var mc = new Roo.util.MixedCollection();
12405 mc.getKey = function(el){
12406    return el.dom.id;
12407 };
12408 mc.add(someEl);
12409 mc.add(otherEl);
12410
12411 // or via the constructor
12412 var mc = new Roo.util.MixedCollection(false, function(el){
12413    return el.dom.id;
12414 });
12415 mc.add(someEl);
12416 mc.add(otherEl);
12417 </code></pre>
12418  * @param o {Object} The item for which to find the key.
12419  * @return {Object} The key for the passed item.
12420  */
12421     getKey : function(o){
12422          return o.id; 
12423     },
12424    
12425 /**
12426  * Replaces an item in the collection.
12427  * @param {String} key The key associated with the item to replace, or the item to replace.
12428  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12429  * @return {Object}  The new item.
12430  */
12431     replace : function(key, o){
12432         if(arguments.length == 1){
12433             o = arguments[0];
12434             key = this.getKey(o);
12435         }
12436         var old = this.item(key);
12437         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12438              return this.add(key, o);
12439         }
12440         var index = this.indexOfKey(key);
12441         this.items[index] = o;
12442         this.map[key] = o;
12443         this.fireEvent("replace", key, old, o);
12444         return o;
12445     },
12446    
12447 /**
12448  * Adds all elements of an Array or an Object to the collection.
12449  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12450  * an Array of values, each of which are added to the collection.
12451  */
12452     addAll : function(objs){
12453         if(arguments.length > 1 || objs instanceof Array){
12454             var args = arguments.length > 1 ? arguments : objs;
12455             for(var i = 0, len = args.length; i < len; i++){
12456                 this.add(args[i]);
12457             }
12458         }else{
12459             for(var key in objs){
12460                 if(this.allowFunctions || typeof objs[key] != "function"){
12461                     this.add(key, objs[key]);
12462                 }
12463             }
12464         }
12465     },
12466    
12467 /**
12468  * Executes the specified function once for every item in the collection, passing each
12469  * item as the first and only parameter. returning false from the function will stop the iteration.
12470  * @param {Function} fn The function to execute for each item.
12471  * @param {Object} scope (optional) The scope in which to execute the function.
12472  */
12473     each : function(fn, scope){
12474         var items = [].concat(this.items); // each safe for removal
12475         for(var i = 0, len = items.length; i < len; i++){
12476             if(fn.call(scope || items[i], items[i], i, len) === false){
12477                 break;
12478             }
12479         }
12480     },
12481    
12482 /**
12483  * Executes the specified function once for every key in the collection, passing each
12484  * key, and its associated item as the first two parameters.
12485  * @param {Function} fn The function to execute for each item.
12486  * @param {Object} scope (optional) The scope in which to execute the function.
12487  */
12488     eachKey : function(fn, scope){
12489         for(var i = 0, len = this.keys.length; i < len; i++){
12490             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12491         }
12492     },
12493    
12494 /**
12495  * Returns the first item in the collection which elicits a true return value from the
12496  * passed selection function.
12497  * @param {Function} fn The selection function to execute for each item.
12498  * @param {Object} scope (optional) The scope in which to execute the function.
12499  * @return {Object} The first item in the collection which returned true from the selection function.
12500  */
12501     find : function(fn, scope){
12502         for(var i = 0, len = this.items.length; i < len; i++){
12503             if(fn.call(scope || window, this.items[i], this.keys[i])){
12504                 return this.items[i];
12505             }
12506         }
12507         return null;
12508     },
12509    
12510 /**
12511  * Inserts an item at the specified index in the collection.
12512  * @param {Number} index The index to insert the item at.
12513  * @param {String} key The key to associate with the new item, or the item itself.
12514  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12515  * @return {Object} The item inserted.
12516  */
12517     insert : function(index, key, o){
12518         if(arguments.length == 2){
12519             o = arguments[1];
12520             key = this.getKey(o);
12521         }
12522         if(index >= this.length){
12523             return this.add(key, o);
12524         }
12525         this.length++;
12526         this.items.splice(index, 0, o);
12527         if(typeof key != "undefined" && key != null){
12528             this.map[key] = o;
12529         }
12530         this.keys.splice(index, 0, key);
12531         this.fireEvent("add", index, o, key);
12532         return o;
12533     },
12534    
12535 /**
12536  * Removed an item from the collection.
12537  * @param {Object} o The item to remove.
12538  * @return {Object} The item removed.
12539  */
12540     remove : function(o){
12541         return this.removeAt(this.indexOf(o));
12542     },
12543    
12544 /**
12545  * Remove an item from a specified index in the collection.
12546  * @param {Number} index The index within the collection of the item to remove.
12547  */
12548     removeAt : function(index){
12549         if(index < this.length && index >= 0){
12550             this.length--;
12551             var o = this.items[index];
12552             this.items.splice(index, 1);
12553             var key = this.keys[index];
12554             if(typeof key != "undefined"){
12555                 delete this.map[key];
12556             }
12557             this.keys.splice(index, 1);
12558             this.fireEvent("remove", o, key);
12559         }
12560     },
12561    
12562 /**
12563  * Removed an item associated with the passed key fom the collection.
12564  * @param {String} key The key of the item to remove.
12565  */
12566     removeKey : function(key){
12567         return this.removeAt(this.indexOfKey(key));
12568     },
12569    
12570 /**
12571  * Returns the number of items in the collection.
12572  * @return {Number} the number of items in the collection.
12573  */
12574     getCount : function(){
12575         return this.length; 
12576     },
12577    
12578 /**
12579  * Returns index within the collection of the passed Object.
12580  * @param {Object} o The item to find the index of.
12581  * @return {Number} index of the item.
12582  */
12583     indexOf : function(o){
12584         if(!this.items.indexOf){
12585             for(var i = 0, len = this.items.length; i < len; i++){
12586                 if(this.items[i] == o) return i;
12587             }
12588             return -1;
12589         }else{
12590             return this.items.indexOf(o);
12591         }
12592     },
12593    
12594 /**
12595  * Returns index within the collection of the passed key.
12596  * @param {String} key The key to find the index of.
12597  * @return {Number} index of the key.
12598  */
12599     indexOfKey : function(key){
12600         if(!this.keys.indexOf){
12601             for(var i = 0, len = this.keys.length; i < len; i++){
12602                 if(this.keys[i] == key) return i;
12603             }
12604             return -1;
12605         }else{
12606             return this.keys.indexOf(key);
12607         }
12608     },
12609    
12610 /**
12611  * Returns the item associated with the passed key OR index. Key has priority over index.
12612  * @param {String/Number} key The key or index of the item.
12613  * @return {Object} The item associated with the passed key.
12614  */
12615     item : function(key){
12616         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12617         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12618     },
12619     
12620 /**
12621  * Returns the item at the specified index.
12622  * @param {Number} index The index of the item.
12623  * @return {Object}
12624  */
12625     itemAt : function(index){
12626         return this.items[index];
12627     },
12628     
12629 /**
12630  * Returns the item associated with the passed key.
12631  * @param {String/Number} key The key of the item.
12632  * @return {Object} The item associated with the passed key.
12633  */
12634     key : function(key){
12635         return this.map[key];
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as an item.
12640  * @param {Object} o  The Object to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as an item.
12642  */
12643     contains : function(o){
12644         return this.indexOf(o) != -1;
12645     },
12646    
12647 /**
12648  * Returns true if the collection contains the passed Object as a key.
12649  * @param {String} key The key to look for in the collection.
12650  * @return {Boolean} True if the collection contains the Object as a key.
12651  */
12652     containsKey : function(key){
12653         return typeof this.map[key] != "undefined";
12654     },
12655    
12656 /**
12657  * Removes all items from the collection.
12658  */
12659     clear : function(){
12660         this.length = 0;
12661         this.items = [];
12662         this.keys = [];
12663         this.map = {};
12664         this.fireEvent("clear");
12665     },
12666    
12667 /**
12668  * Returns the first item in the collection.
12669  * @return {Object} the first item in the collection..
12670  */
12671     first : function(){
12672         return this.items[0]; 
12673     },
12674    
12675 /**
12676  * Returns the last item in the collection.
12677  * @return {Object} the last item in the collection..
12678  */
12679     last : function(){
12680         return this.items[this.length-1];   
12681     },
12682     
12683     _sort : function(property, dir, fn){
12684         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12685         fn = fn || function(a, b){
12686             return a-b;
12687         };
12688         var c = [], k = this.keys, items = this.items;
12689         for(var i = 0, len = items.length; i < len; i++){
12690             c[c.length] = {key: k[i], value: items[i], index: i};
12691         }
12692         c.sort(function(a, b){
12693             var v = fn(a[property], b[property]) * dsc;
12694             if(v == 0){
12695                 v = (a.index < b.index ? -1 : 1);
12696             }
12697             return v;
12698         });
12699         for(var i = 0, len = c.length; i < len; i++){
12700             items[i] = c[i].value;
12701             k[i] = c[i].key;
12702         }
12703         this.fireEvent("sort", this);
12704     },
12705     
12706     /**
12707      * Sorts this collection with the passed comparison function
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) comparison function
12710      */
12711     sort : function(dir, fn){
12712         this._sort("value", dir, fn);
12713     },
12714     
12715     /**
12716      * Sorts this collection by keys
12717      * @param {String} direction (optional) "ASC" or "DESC"
12718      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12719      */
12720     keySort : function(dir, fn){
12721         this._sort("key", dir, fn || function(a, b){
12722             return String(a).toUpperCase()-String(b).toUpperCase();
12723         });
12724     },
12725     
12726     /**
12727      * Returns a range of items in this collection
12728      * @param {Number} startIndex (optional) defaults to 0
12729      * @param {Number} endIndex (optional) default to the last item
12730      * @return {Array} An array of items
12731      */
12732     getRange : function(start, end){
12733         var items = this.items;
12734         if(items.length < 1){
12735             return [];
12736         }
12737         start = start || 0;
12738         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12739         var r = [];
12740         if(start <= end){
12741             for(var i = start; i <= end; i++) {
12742                     r[r.length] = items[i];
12743             }
12744         }else{
12745             for(var i = start; i >= end; i--) {
12746                     r[r.length] = items[i];
12747             }
12748         }
12749         return r;
12750     },
12751         
12752     /**
12753      * Filter the <i>objects</i> in this collection by a specific property. 
12754      * Returns a new collection that has been filtered.
12755      * @param {String} property A property on your objects
12756      * @param {String/RegExp} value Either string that the property values 
12757      * should start with or a RegExp to test against the property
12758      * @return {MixedCollection} The new filtered collection
12759      */
12760     filter : function(property, value){
12761         if(!value.exec){ // not a regex
12762             value = String(value);
12763             if(value.length == 0){
12764                 return this.clone();
12765             }
12766             value = new RegExp("^" + Roo.escapeRe(value), "i");
12767         }
12768         return this.filterBy(function(o){
12769             return o && value.test(o[property]);
12770         });
12771         },
12772     
12773     /**
12774      * Filter by a function. * Returns a new collection that has been filtered.
12775      * The passed function will be called with each 
12776      * object in the collection. If the function returns true, the value is included 
12777      * otherwise it is filtered.
12778      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12779      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12780      * @return {MixedCollection} The new filtered collection
12781      */
12782     filterBy : function(fn, scope){
12783         var r = new Roo.util.MixedCollection();
12784         r.getKey = this.getKey;
12785         var k = this.keys, it = this.items;
12786         for(var i = 0, len = it.length; i < len; i++){
12787             if(fn.call(scope||this, it[i], k[i])){
12788                                 r.add(k[i], it[i]);
12789                         }
12790         }
12791         return r;
12792     },
12793     
12794     /**
12795      * Creates a duplicate of this collection
12796      * @return {MixedCollection}
12797      */
12798     clone : function(){
12799         var r = new Roo.util.MixedCollection();
12800         var k = this.keys, it = this.items;
12801         for(var i = 0, len = it.length; i < len; i++){
12802             r.add(k[i], it[i]);
12803         }
12804         r.getKey = this.getKey;
12805         return r;
12806     }
12807 });
12808 /**
12809  * Returns the item associated with the passed key or index.
12810  * @method
12811  * @param {String/Number} key The key or index of the item.
12812  * @return {Object} The item associated with the passed key.
12813  */
12814 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12815  * Based on:
12816  * Ext JS Library 1.1.1
12817  * Copyright(c) 2006-2007, Ext JS, LLC.
12818  *
12819  * Originally Released Under LGPL - original licence link has changed is not relivant.
12820  *
12821  * Fork - LGPL
12822  * <script type="text/javascript">
12823  */
12824 /**
12825  * @class Roo.util.JSON
12826  * Modified version of Douglas Crockford"s json.js that doesn"t
12827  * mess with the Object prototype 
12828  * http://www.json.org/js.html
12829  * @singleton
12830  */
12831 Roo.util.JSON = new (function(){
12832     var useHasOwn = {}.hasOwnProperty ? true : false;
12833     
12834     // crashes Safari in some instances
12835     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12836     
12837     var pad = function(n) {
12838         return n < 10 ? "0" + n : n;
12839     };
12840     
12841     var m = {
12842         "\b": '\\b',
12843         "\t": '\\t',
12844         "\n": '\\n',
12845         "\f": '\\f',
12846         "\r": '\\r',
12847         '"' : '\\"',
12848         "\\": '\\\\'
12849     };
12850
12851     var encodeString = function(s){
12852         if (/["\\\x00-\x1f]/.test(s)) {
12853             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12854                 var c = m[b];
12855                 if(c){
12856                     return c;
12857                 }
12858                 c = b.charCodeAt();
12859                 return "\\u00" +
12860                     Math.floor(c / 16).toString(16) +
12861                     (c % 16).toString(16);
12862             }) + '"';
12863         }
12864         return '"' + s + '"';
12865     };
12866     
12867     var encodeArray = function(o){
12868         var a = ["["], b, i, l = o.length, v;
12869             for (i = 0; i < l; i += 1) {
12870                 v = o[i];
12871                 switch (typeof v) {
12872                     case "undefined":
12873                     case "function":
12874                     case "unknown":
12875                         break;
12876                     default:
12877                         if (b) {
12878                             a.push(',');
12879                         }
12880                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12881                         b = true;
12882                 }
12883             }
12884             a.push("]");
12885             return a.join("");
12886     };
12887     
12888     var encodeDate = function(o){
12889         return '"' + o.getFullYear() + "-" +
12890                 pad(o.getMonth() + 1) + "-" +
12891                 pad(o.getDate()) + "T" +
12892                 pad(o.getHours()) + ":" +
12893                 pad(o.getMinutes()) + ":" +
12894                 pad(o.getSeconds()) + '"';
12895     };
12896     
12897     /**
12898      * Encodes an Object, Array or other value
12899      * @param {Mixed} o The variable to encode
12900      * @return {String} The JSON string
12901      */
12902     this.encode = function(o)
12903     {
12904         // should this be extended to fully wrap stringify..
12905         
12906         if(typeof o == "undefined" || o === null){
12907             return "null";
12908         }else if(o instanceof Array){
12909             return encodeArray(o);
12910         }else if(o instanceof Date){
12911             return encodeDate(o);
12912         }else if(typeof o == "string"){
12913             return encodeString(o);
12914         }else if(typeof o == "number"){
12915             return isFinite(o) ? String(o) : "null";
12916         }else if(typeof o == "boolean"){
12917             return String(o);
12918         }else {
12919             var a = ["{"], b, i, v;
12920             for (i in o) {
12921                 if(!useHasOwn || o.hasOwnProperty(i)) {
12922                     v = o[i];
12923                     switch (typeof v) {
12924                     case "undefined":
12925                     case "function":
12926                     case "unknown":
12927                         break;
12928                     default:
12929                         if(b){
12930                             a.push(',');
12931                         }
12932                         a.push(this.encode(i), ":",
12933                                 v === null ? "null" : this.encode(v));
12934                         b = true;
12935                     }
12936                 }
12937             }
12938             a.push("}");
12939             return a.join("");
12940         }
12941     };
12942     
12943     /**
12944      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12945      * @param {String} json The JSON string
12946      * @return {Object} The resulting object
12947      */
12948     this.decode = function(json){
12949         
12950         return  /** eval:var:json */ eval("(" + json + ')');
12951     };
12952 })();
12953 /** 
12954  * Shorthand for {@link Roo.util.JSON#encode}
12955  * @member Roo encode 
12956  * @method */
12957 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12958 /** 
12959  * Shorthand for {@link Roo.util.JSON#decode}
12960  * @member Roo decode 
12961  * @method */
12962 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12963 /*
12964  * Based on:
12965  * Ext JS Library 1.1.1
12966  * Copyright(c) 2006-2007, Ext JS, LLC.
12967  *
12968  * Originally Released Under LGPL - original licence link has changed is not relivant.
12969  *
12970  * Fork - LGPL
12971  * <script type="text/javascript">
12972  */
12973  
12974 /**
12975  * @class Roo.util.Format
12976  * Reusable data formatting functions
12977  * @singleton
12978  */
12979 Roo.util.Format = function(){
12980     var trimRe = /^\s+|\s+$/g;
12981     return {
12982         /**
12983          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12984          * @param {String} value The string to truncate
12985          * @param {Number} length The maximum length to allow before truncating
12986          * @return {String} The converted text
12987          */
12988         ellipsis : function(value, len){
12989             if(value && value.length > len){
12990                 return value.substr(0, len-3)+"...";
12991             }
12992             return value;
12993         },
12994
12995         /**
12996          * Checks a reference and converts it to empty string if it is undefined
12997          * @param {Mixed} value Reference to check
12998          * @return {Mixed} Empty string if converted, otherwise the original value
12999          */
13000         undef : function(value){
13001             return typeof value != "undefined" ? value : "";
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13006          * @param {String} value The string to encode
13007          * @return {String} The encoded text
13008          */
13009         htmlEncode : function(value){
13010             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13011         },
13012
13013         /**
13014          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13015          * @param {String} value The string to decode
13016          * @return {String} The decoded text
13017          */
13018         htmlDecode : function(value){
13019             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13020         },
13021
13022         /**
13023          * Trims any whitespace from either side of a string
13024          * @param {String} value The text to trim
13025          * @return {String} The trimmed text
13026          */
13027         trim : function(value){
13028             return String(value).replace(trimRe, "");
13029         },
13030
13031         /**
13032          * Returns a substring from within an original string
13033          * @param {String} value The original text
13034          * @param {Number} start The start index of the substring
13035          * @param {Number} length The length of the substring
13036          * @return {String} The substring
13037          */
13038         substr : function(value, start, length){
13039             return String(value).substr(start, length);
13040         },
13041
13042         /**
13043          * Converts a string to all lower case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         lowercase : function(value){
13048             return String(value).toLowerCase();
13049         },
13050
13051         /**
13052          * Converts a string to all upper case letters
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         uppercase : function(value){
13057             return String(value).toUpperCase();
13058         },
13059
13060         /**
13061          * Converts the first character only of a string to upper case
13062          * @param {String} value The text to convert
13063          * @return {String} The converted text
13064          */
13065         capitalize : function(value){
13066             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13067         },
13068
13069         // private
13070         call : function(value, fn){
13071             if(arguments.length > 2){
13072                 var args = Array.prototype.slice.call(arguments, 2);
13073                 args.unshift(value);
13074                  
13075                 return /** eval:var:value */  eval(fn).apply(window, args);
13076             }else{
13077                 /** eval:var:value */
13078                 return /** eval:var:value */ eval(fn).call(window, value);
13079             }
13080         },
13081
13082        
13083         /**
13084          * safer version of Math.toFixed..??/
13085          * @param {Number/String} value The numeric value to format
13086          * @param {Number/String} value Decimal places 
13087          * @return {String} The formatted currency string
13088          */
13089         toFixed : function(v, n)
13090         {
13091             // why not use to fixed - precision is buggered???
13092             if (!n) {
13093                 return Math.round(v-0);
13094             }
13095             var fact = Math.pow(10,n+1);
13096             v = (Math.round((v-0)*fact))/fact;
13097             var z = (''+fact).substring(2);
13098             if (v == Math.floor(v)) {
13099                 return Math.floor(v) + '.' + z;
13100             }
13101             
13102             // now just padd decimals..
13103             var ps = String(v).split('.');
13104             var fd = (ps[1] + z);
13105             var r = fd.substring(0,n); 
13106             var rm = fd.substring(n); 
13107             if (rm < 5) {
13108                 return ps[0] + '.' + r;
13109             }
13110             r*=1; // turn it into a number;
13111             r++;
13112             if (String(r).length != n) {
13113                 ps[0]*=1;
13114                 ps[0]++;
13115                 r = String(r).substring(1); // chop the end off.
13116             }
13117             
13118             return ps[0] + '.' + r;
13119              
13120         },
13121         
13122         /**
13123          * Format a number as US currency
13124          * @param {Number/String} value The numeric value to format
13125          * @return {String} The formatted currency string
13126          */
13127         usMoney : function(v){
13128             v = (Math.round((v-0)*100))/100;
13129             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13130             v = String(v);
13131             var ps = v.split('.');
13132             var whole = ps[0];
13133             var sub = ps[1] ? '.'+ ps[1] : '.00';
13134             var r = /(\d+)(\d{3})/;
13135             while (r.test(whole)) {
13136                 whole = whole.replace(r, '$1' + ',' + '$2');
13137             }
13138             return "$" + whole + sub ;
13139         },
13140         
13141         /**
13142          * Parse a value into a formatted date using the specified format pattern.
13143          * @param {Mixed} value The value to format
13144          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13145          * @return {String} The formatted date string
13146          */
13147         date : function(v, format){
13148             if(!v){
13149                 return "";
13150             }
13151             if(!(v instanceof Date)){
13152                 v = new Date(Date.parse(v));
13153             }
13154             return v.dateFormat(format || "m/d/Y");
13155         },
13156
13157         /**
13158          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13159          * @param {String} format Any valid date format string
13160          * @return {Function} The date formatting function
13161          */
13162         dateRenderer : function(format){
13163             return function(v){
13164                 return Roo.util.Format.date(v, format);  
13165             };
13166         },
13167
13168         // private
13169         stripTagsRE : /<\/?[^>]+>/gi,
13170         
13171         /**
13172          * Strips all HTML tags
13173          * @param {Mixed} value The text from which to strip tags
13174          * @return {String} The stripped text
13175          */
13176         stripTags : function(v){
13177             return !v ? v : String(v).replace(this.stripTagsRE, "");
13178         }
13179     };
13180 }();/*
13181  * Based on:
13182  * Ext JS Library 1.1.1
13183  * Copyright(c) 2006-2007, Ext JS, LLC.
13184  *
13185  * Originally Released Under LGPL - original licence link has changed is not relivant.
13186  *
13187  * Fork - LGPL
13188  * <script type="text/javascript">
13189  */
13190
13191
13192  
13193
13194 /**
13195  * @class Roo.MasterTemplate
13196  * @extends Roo.Template
13197  * Provides a template that can have child templates. The syntax is:
13198 <pre><code>
13199 var t = new Roo.MasterTemplate(
13200         '&lt;select name="{name}"&gt;',
13201                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13202         '&lt;/select&gt;'
13203 );
13204 t.add('options', {value: 'foo', text: 'bar'});
13205 // or you can add multiple child elements in one shot
13206 t.addAll('options', [
13207     {value: 'foo', text: 'bar'},
13208     {value: 'foo2', text: 'bar2'},
13209     {value: 'foo3', text: 'bar3'}
13210 ]);
13211 // then append, applying the master template values
13212 t.append('my-form', {name: 'my-select'});
13213 </code></pre>
13214 * A name attribute for the child template is not required if you have only one child
13215 * template or you want to refer to them by index.
13216  */
13217 Roo.MasterTemplate = function(){
13218     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13219     this.originalHtml = this.html;
13220     var st = {};
13221     var m, re = this.subTemplateRe;
13222     re.lastIndex = 0;
13223     var subIndex = 0;
13224     while(m = re.exec(this.html)){
13225         var name = m[1], content = m[2];
13226         st[subIndex] = {
13227             name: name,
13228             index: subIndex,
13229             buffer: [],
13230             tpl : new Roo.Template(content)
13231         };
13232         if(name){
13233             st[name] = st[subIndex];
13234         }
13235         st[subIndex].tpl.compile();
13236         st[subIndex].tpl.call = this.call.createDelegate(this);
13237         subIndex++;
13238     }
13239     this.subCount = subIndex;
13240     this.subs = st;
13241 };
13242 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13243     /**
13244     * The regular expression used to match sub templates
13245     * @type RegExp
13246     * @property
13247     */
13248     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13249
13250     /**
13251      * Applies the passed values to a child template.
13252      * @param {String/Number} name (optional) The name or index of the child template
13253      * @param {Array/Object} values The values to be applied to the template
13254      * @return {MasterTemplate} this
13255      */
13256      add : function(name, values){
13257         if(arguments.length == 1){
13258             values = arguments[0];
13259             name = 0;
13260         }
13261         var s = this.subs[name];
13262         s.buffer[s.buffer.length] = s.tpl.apply(values);
13263         return this;
13264     },
13265
13266     /**
13267      * Applies all the passed values to a child template.
13268      * @param {String/Number} name (optional) The name or index of the child template
13269      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13270      * @param {Boolean} reset (optional) True to reset the template first
13271      * @return {MasterTemplate} this
13272      */
13273     fill : function(name, values, reset){
13274         var a = arguments;
13275         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13276             values = a[0];
13277             name = 0;
13278             reset = a[1];
13279         }
13280         if(reset){
13281             this.reset();
13282         }
13283         for(var i = 0, len = values.length; i < len; i++){
13284             this.add(name, values[i]);
13285         }
13286         return this;
13287     },
13288
13289     /**
13290      * Resets the template for reuse
13291      * @return {MasterTemplate} this
13292      */
13293      reset : function(){
13294         var s = this.subs;
13295         for(var i = 0; i < this.subCount; i++){
13296             s[i].buffer = [];
13297         }
13298         return this;
13299     },
13300
13301     applyTemplate : function(values){
13302         var s = this.subs;
13303         var replaceIndex = -1;
13304         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13305             return s[++replaceIndex].buffer.join("");
13306         });
13307         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13308     },
13309
13310     apply : function(){
13311         return this.applyTemplate.apply(this, arguments);
13312     },
13313
13314     compile : function(){return this;}
13315 });
13316
13317 /**
13318  * Alias for fill().
13319  * @method
13320  */
13321 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13322  /**
13323  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13324  * var tpl = Roo.MasterTemplate.from('element-id');
13325  * @param {String/HTMLElement} el
13326  * @param {Object} config
13327  * @static
13328  */
13329 Roo.MasterTemplate.from = function(el, config){
13330     el = Roo.getDom(el);
13331     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13332 };/*
13333  * Based on:
13334  * Ext JS Library 1.1.1
13335  * Copyright(c) 2006-2007, Ext JS, LLC.
13336  *
13337  * Originally Released Under LGPL - original licence link has changed is not relivant.
13338  *
13339  * Fork - LGPL
13340  * <script type="text/javascript">
13341  */
13342
13343  
13344 /**
13345  * @class Roo.util.CSS
13346  * Utility class for manipulating CSS rules
13347  * @singleton
13348  */
13349 Roo.util.CSS = function(){
13350         var rules = null;
13351         var doc = document;
13352
13353     var camelRe = /(-[a-z])/gi;
13354     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13355
13356    return {
13357    /**
13358     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13359     * tag and appended to the HEAD of the document.
13360     * @param {String|Object} cssText The text containing the css rules
13361     * @param {String} id An id to add to the stylesheet for later removal
13362     * @return {StyleSheet}
13363     */
13364     createStyleSheet : function(cssText, id){
13365         var ss;
13366         var head = doc.getElementsByTagName("head")[0];
13367         var nrules = doc.createElement("style");
13368         nrules.setAttribute("type", "text/css");
13369         if(id){
13370             nrules.setAttribute("id", id);
13371         }
13372         if (typeof(cssText) != 'string') {
13373             // support object maps..
13374             // not sure if this a good idea.. 
13375             // perhaps it should be merged with the general css handling
13376             // and handle js style props.
13377             var cssTextNew = [];
13378             for(var n in cssText) {
13379                 var citems = [];
13380                 for(var k in cssText[n]) {
13381                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13382                 }
13383                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13384                 
13385             }
13386             cssText = cssTextNew.join("\n");
13387             
13388         }
13389        
13390        
13391        if(Roo.isIE){
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet;
13394            ss.cssText = cssText;
13395        }else{
13396            try{
13397                 nrules.appendChild(doc.createTextNode(cssText));
13398            }catch(e){
13399                nrules.cssText = cssText; 
13400            }
13401            head.appendChild(nrules);
13402            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13403        }
13404        this.cacheStyleSheet(ss);
13405        return ss;
13406    },
13407
13408    /**
13409     * Removes a style or link tag by id
13410     * @param {String} id The id of the tag
13411     */
13412    removeStyleSheet : function(id){
13413        var existing = doc.getElementById(id);
13414        if(existing){
13415            existing.parentNode.removeChild(existing);
13416        }
13417    },
13418
13419    /**
13420     * Dynamically swaps an existing stylesheet reference for a new one
13421     * @param {String} id The id of an existing link tag to remove
13422     * @param {String} url The href of the new stylesheet to include
13423     */
13424    swapStyleSheet : function(id, url){
13425        this.removeStyleSheet(id);
13426        var ss = doc.createElement("link");
13427        ss.setAttribute("rel", "stylesheet");
13428        ss.setAttribute("type", "text/css");
13429        ss.setAttribute("id", id);
13430        ss.setAttribute("href", url);
13431        doc.getElementsByTagName("head")[0].appendChild(ss);
13432    },
13433    
13434    /**
13435     * Refresh the rule cache if you have dynamically added stylesheets
13436     * @return {Object} An object (hash) of rules indexed by selector
13437     */
13438    refreshCache : function(){
13439        return this.getRules(true);
13440    },
13441
13442    // private
13443    cacheStyleSheet : function(stylesheet){
13444        if(!rules){
13445            rules = {};
13446        }
13447        try{// try catch for cross domain access issue
13448            var ssRules = stylesheet.cssRules || stylesheet.rules;
13449            for(var j = ssRules.length-1; j >= 0; --j){
13450                rules[ssRules[j].selectorText] = ssRules[j];
13451            }
13452        }catch(e){}
13453    },
13454    
13455    /**
13456     * Gets all css rules for the document
13457     * @param {Boolean} refreshCache true to refresh the internal cache
13458     * @return {Object} An object (hash) of rules indexed by selector
13459     */
13460    getRules : function(refreshCache){
13461                 if(rules == null || refreshCache){
13462                         rules = {};
13463                         var ds = doc.styleSheets;
13464                         for(var i =0, len = ds.length; i < len; i++){
13465                             try{
13466                         this.cacheStyleSheet(ds[i]);
13467                     }catch(e){} 
13468                 }
13469                 }
13470                 return rules;
13471         },
13472         
13473         /**
13474     * Gets an an individual CSS rule by selector(s)
13475     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13476     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13477     * @return {CSSRule} The CSS rule or null if one is not found
13478     */
13479    getRule : function(selector, refreshCache){
13480                 var rs = this.getRules(refreshCache);
13481                 if(!(selector instanceof Array)){
13482                     return rs[selector];
13483                 }
13484                 for(var i = 0; i < selector.length; i++){
13485                         if(rs[selector[i]]){
13486                                 return rs[selector[i]];
13487                         }
13488                 }
13489                 return null;
13490         },
13491         
13492         
13493         /**
13494     * Updates a rule property
13495     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13496     * @param {String} property The css property
13497     * @param {String} value The new value for the property
13498     * @return {Boolean} true If a rule was found and updated
13499     */
13500    updateRule : function(selector, property, value){
13501                 if(!(selector instanceof Array)){
13502                         var rule = this.getRule(selector);
13503                         if(rule){
13504                                 rule.style[property.replace(camelRe, camelFn)] = value;
13505                                 return true;
13506                         }
13507                 }else{
13508                         for(var i = 0; i < selector.length; i++){
13509                                 if(this.updateRule(selector[i], property, value)){
13510                                         return true;
13511                                 }
13512                         }
13513                 }
13514                 return false;
13515         }
13516    };   
13517 }();/*
13518  * Based on:
13519  * Ext JS Library 1.1.1
13520  * Copyright(c) 2006-2007, Ext JS, LLC.
13521  *
13522  * Originally Released Under LGPL - original licence link has changed is not relivant.
13523  *
13524  * Fork - LGPL
13525  * <script type="text/javascript">
13526  */
13527
13528  
13529
13530 /**
13531  * @class Roo.util.ClickRepeater
13532  * @extends Roo.util.Observable
13533  * 
13534  * A wrapper class which can be applied to any element. Fires a "click" event while the
13535  * mouse is pressed. The interval between firings may be specified in the config but
13536  * defaults to 10 milliseconds.
13537  * 
13538  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13539  * 
13540  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13541  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13542  * Similar to an autorepeat key delay.
13543  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13544  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13545  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13546  *           "interval" and "delay" are ignored. "immediate" is honored.
13547  * @cfg {Boolean} preventDefault True to prevent the default click event
13548  * @cfg {Boolean} stopDefault True to stop the default click event
13549  * 
13550  * @history
13551  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13552  *     2007-02-02 jvs Renamed to ClickRepeater
13553  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13554  *
13555  *  @constructor
13556  * @param {String/HTMLElement/Element} el The element to listen on
13557  * @param {Object} config
13558  **/
13559 Roo.util.ClickRepeater = function(el, config)
13560 {
13561     this.el = Roo.get(el);
13562     this.el.unselectable();
13563
13564     Roo.apply(this, config);
13565
13566     this.addEvents({
13567     /**
13568      * @event mousedown
13569      * Fires when the mouse button is depressed.
13570      * @param {Roo.util.ClickRepeater} this
13571      */
13572         "mousedown" : true,
13573     /**
13574      * @event click
13575      * Fires on a specified interval during the time the element is pressed.
13576      * @param {Roo.util.ClickRepeater} this
13577      */
13578         "click" : true,
13579     /**
13580      * @event mouseup
13581      * Fires when the mouse key is released.
13582      * @param {Roo.util.ClickRepeater} this
13583      */
13584         "mouseup" : true
13585     });
13586
13587     this.el.on("mousedown", this.handleMouseDown, this);
13588     if(this.preventDefault || this.stopDefault){
13589         this.el.on("click", function(e){
13590             if(this.preventDefault){
13591                 e.preventDefault();
13592             }
13593             if(this.stopDefault){
13594                 e.stopEvent();
13595             }
13596         }, this);
13597     }
13598
13599     // allow inline handler
13600     if(this.handler){
13601         this.on("click", this.handler,  this.scope || this);
13602     }
13603
13604     Roo.util.ClickRepeater.superclass.constructor.call(this);
13605 };
13606
13607 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13608     interval : 20,
13609     delay: 250,
13610     preventDefault : true,
13611     stopDefault : false,
13612     timer : 0,
13613
13614     // private
13615     handleMouseDown : function(){
13616         clearTimeout(this.timer);
13617         this.el.blur();
13618         if(this.pressClass){
13619             this.el.addClass(this.pressClass);
13620         }
13621         this.mousedownTime = new Date();
13622
13623         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13624         this.el.on("mouseout", this.handleMouseOut, this);
13625
13626         this.fireEvent("mousedown", this);
13627         this.fireEvent("click", this);
13628         
13629         this.timer = this.click.defer(this.delay || this.interval, this);
13630     },
13631
13632     // private
13633     click : function(){
13634         this.fireEvent("click", this);
13635         this.timer = this.click.defer(this.getInterval(), this);
13636     },
13637
13638     // private
13639     getInterval: function(){
13640         if(!this.accelerate){
13641             return this.interval;
13642         }
13643         var pressTime = this.mousedownTime.getElapsed();
13644         if(pressTime < 500){
13645             return 400;
13646         }else if(pressTime < 1700){
13647             return 320;
13648         }else if(pressTime < 2600){
13649             return 250;
13650         }else if(pressTime < 3500){
13651             return 180;
13652         }else if(pressTime < 4400){
13653             return 140;
13654         }else if(pressTime < 5300){
13655             return 80;
13656         }else if(pressTime < 6200){
13657             return 50;
13658         }else{
13659             return 10;
13660         }
13661     },
13662
13663     // private
13664     handleMouseOut : function(){
13665         clearTimeout(this.timer);
13666         if(this.pressClass){
13667             this.el.removeClass(this.pressClass);
13668         }
13669         this.el.on("mouseover", this.handleMouseReturn, this);
13670     },
13671
13672     // private
13673     handleMouseReturn : function(){
13674         this.el.un("mouseover", this.handleMouseReturn);
13675         if(this.pressClass){
13676             this.el.addClass(this.pressClass);
13677         }
13678         this.click();
13679     },
13680
13681     // private
13682     handleMouseUp : function(){
13683         clearTimeout(this.timer);
13684         this.el.un("mouseover", this.handleMouseReturn);
13685         this.el.un("mouseout", this.handleMouseOut);
13686         Roo.get(document).un("mouseup", this.handleMouseUp);
13687         this.el.removeClass(this.pressClass);
13688         this.fireEvent("mouseup", this);
13689     }
13690 });/*
13691  * Based on:
13692  * Ext JS Library 1.1.1
13693  * Copyright(c) 2006-2007, Ext JS, LLC.
13694  *
13695  * Originally Released Under LGPL - original licence link has changed is not relivant.
13696  *
13697  * Fork - LGPL
13698  * <script type="text/javascript">
13699  */
13700
13701  
13702 /**
13703  * @class Roo.KeyNav
13704  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13705  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13706  * way to implement custom navigation schemes for any UI component.</p>
13707  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13708  * pageUp, pageDown, del, home, end.  Usage:</p>
13709  <pre><code>
13710 var nav = new Roo.KeyNav("my-element", {
13711     "left" : function(e){
13712         this.moveLeft(e.ctrlKey);
13713     },
13714     "right" : function(e){
13715         this.moveRight(e.ctrlKey);
13716     },
13717     "enter" : function(e){
13718         this.save();
13719     },
13720     scope : this
13721 });
13722 </code></pre>
13723  * @constructor
13724  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13725  * @param {Object} config The config
13726  */
13727 Roo.KeyNav = function(el, config){
13728     this.el = Roo.get(el);
13729     Roo.apply(this, config);
13730     if(!this.disabled){
13731         this.disabled = true;
13732         this.enable();
13733     }
13734 };
13735
13736 Roo.KeyNav.prototype = {
13737     /**
13738      * @cfg {Boolean} disabled
13739      * True to disable this KeyNav instance (defaults to false)
13740      */
13741     disabled : false,
13742     /**
13743      * @cfg {String} defaultEventAction
13744      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13745      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13746      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13747      */
13748     defaultEventAction: "stopEvent",
13749     /**
13750      * @cfg {Boolean} forceKeyDown
13751      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13752      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13753      * handle keydown instead of keypress.
13754      */
13755     forceKeyDown : false,
13756
13757     // private
13758     prepareEvent : function(e){
13759         var k = e.getKey();
13760         var h = this.keyToHandler[k];
13761         //if(h && this[h]){
13762         //    e.stopPropagation();
13763         //}
13764         if(Roo.isSafari && h && k >= 37 && k <= 40){
13765             e.stopEvent();
13766         }
13767     },
13768
13769     // private
13770     relay : function(e){
13771         var k = e.getKey();
13772         var h = this.keyToHandler[k];
13773         if(h && this[h]){
13774             if(this.doRelay(e, this[h], h) !== true){
13775                 e[this.defaultEventAction]();
13776             }
13777         }
13778     },
13779
13780     // private
13781     doRelay : function(e, h, hname){
13782         return h.call(this.scope || this, e);
13783     },
13784
13785     // possible handlers
13786     enter : false,
13787     left : false,
13788     right : false,
13789     up : false,
13790     down : false,
13791     tab : false,
13792     esc : false,
13793     pageUp : false,
13794     pageDown : false,
13795     del : false,
13796     home : false,
13797     end : false,
13798
13799     // quick lookup hash
13800     keyToHandler : {
13801         37 : "left",
13802         39 : "right",
13803         38 : "up",
13804         40 : "down",
13805         33 : "pageUp",
13806         34 : "pageDown",
13807         46 : "del",
13808         36 : "home",
13809         35 : "end",
13810         13 : "enter",
13811         27 : "esc",
13812         9  : "tab"
13813     },
13814
13815         /**
13816          * Enable this KeyNav
13817          */
13818         enable: function(){
13819                 if(this.disabled){
13820             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13821             // the EventObject will normalize Safari automatically
13822             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13823                 this.el.on("keydown", this.relay,  this);
13824             }else{
13825                 this.el.on("keydown", this.prepareEvent,  this);
13826                 this.el.on("keypress", this.relay,  this);
13827             }
13828                     this.disabled = false;
13829                 }
13830         },
13831
13832         /**
13833          * Disable this KeyNav
13834          */
13835         disable: function(){
13836                 if(!this.disabled){
13837                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13838                 this.el.un("keydown", this.relay);
13839             }else{
13840                 this.el.un("keydown", this.prepareEvent);
13841                 this.el.un("keypress", this.relay);
13842             }
13843                     this.disabled = true;
13844                 }
13845         }
13846 };/*
13847  * Based on:
13848  * Ext JS Library 1.1.1
13849  * Copyright(c) 2006-2007, Ext JS, LLC.
13850  *
13851  * Originally Released Under LGPL - original licence link has changed is not relivant.
13852  *
13853  * Fork - LGPL
13854  * <script type="text/javascript">
13855  */
13856
13857  
13858 /**
13859  * @class Roo.KeyMap
13860  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13861  * The constructor accepts the same config object as defined by {@link #addBinding}.
13862  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13863  * combination it will call the function with this signature (if the match is a multi-key
13864  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13865  * A KeyMap can also handle a string representation of keys.<br />
13866  * Usage:
13867  <pre><code>
13868 // map one key by key code
13869 var map = new Roo.KeyMap("my-element", {
13870     key: 13, // or Roo.EventObject.ENTER
13871     fn: myHandler,
13872     scope: myObject
13873 });
13874
13875 // map multiple keys to one action by string
13876 var map = new Roo.KeyMap("my-element", {
13877     key: "a\r\n\t",
13878     fn: myHandler,
13879     scope: myObject
13880 });
13881
13882 // map multiple keys to multiple actions by strings and array of codes
13883 var map = new Roo.KeyMap("my-element", [
13884     {
13885         key: [10,13],
13886         fn: function(){ alert("Return was pressed"); }
13887     }, {
13888         key: "abc",
13889         fn: function(){ alert('a, b or c was pressed'); }
13890     }, {
13891         key: "\t",
13892         ctrl:true,
13893         shift:true,
13894         fn: function(){ alert('Control + shift + tab was pressed.'); }
13895     }
13896 ]);
13897 </code></pre>
13898  * <b>Note: A KeyMap starts enabled</b>
13899  * @constructor
13900  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13901  * @param {Object} config The config (see {@link #addBinding})
13902  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13903  */
13904 Roo.KeyMap = function(el, config, eventName){
13905     this.el  = Roo.get(el);
13906     this.eventName = eventName || "keydown";
13907     this.bindings = [];
13908     if(config){
13909         this.addBinding(config);
13910     }
13911     this.enable();
13912 };
13913
13914 Roo.KeyMap.prototype = {
13915     /**
13916      * True to stop the event from bubbling and prevent the default browser action if the
13917      * key was handled by the KeyMap (defaults to false)
13918      * @type Boolean
13919      */
13920     stopEvent : false,
13921
13922     /**
13923      * Add a new binding to this KeyMap. The following config object properties are supported:
13924      * <pre>
13925 Property    Type             Description
13926 ----------  ---------------  ----------------------------------------------------------------------
13927 key         String/Array     A single keycode or an array of keycodes to handle
13928 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13929 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13930 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13931 fn          Function         The function to call when KeyMap finds the expected key combination
13932 scope       Object           The scope of the callback function
13933 </pre>
13934      *
13935      * Usage:
13936      * <pre><code>
13937 // Create a KeyMap
13938 var map = new Roo.KeyMap(document, {
13939     key: Roo.EventObject.ENTER,
13940     fn: handleKey,
13941     scope: this
13942 });
13943
13944 //Add a new binding to the existing KeyMap later
13945 map.addBinding({
13946     key: 'abc',
13947     shift: true,
13948     fn: handleKey,
13949     scope: this
13950 });
13951 </code></pre>
13952      * @param {Object/Array} config A single KeyMap config or an array of configs
13953      */
13954         addBinding : function(config){
13955         if(config instanceof Array){
13956             for(var i = 0, len = config.length; i < len; i++){
13957                 this.addBinding(config[i]);
13958             }
13959             return;
13960         }
13961         var keyCode = config.key,
13962             shift = config.shift, 
13963             ctrl = config.ctrl, 
13964             alt = config.alt,
13965             fn = config.fn,
13966             scope = config.scope;
13967         if(typeof keyCode == "string"){
13968             var ks = [];
13969             var keyString = keyCode.toUpperCase();
13970             for(var j = 0, len = keyString.length; j < len; j++){
13971                 ks.push(keyString.charCodeAt(j));
13972             }
13973             keyCode = ks;
13974         }
13975         var keyArray = keyCode instanceof Array;
13976         var handler = function(e){
13977             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13978                 var k = e.getKey();
13979                 if(keyArray){
13980                     for(var i = 0, len = keyCode.length; i < len; i++){
13981                         if(keyCode[i] == k){
13982                           if(this.stopEvent){
13983                               e.stopEvent();
13984                           }
13985                           fn.call(scope || window, k, e);
13986                           return;
13987                         }
13988                     }
13989                 }else{
13990                     if(k == keyCode){
13991                         if(this.stopEvent){
13992                            e.stopEvent();
13993                         }
13994                         fn.call(scope || window, k, e);
13995                     }
13996                 }
13997             }
13998         };
13999         this.bindings.push(handler);  
14000         },
14001
14002     /**
14003      * Shorthand for adding a single key listener
14004      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14005      * following options:
14006      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14007      * @param {Function} fn The function to call
14008      * @param {Object} scope (optional) The scope of the function
14009      */
14010     on : function(key, fn, scope){
14011         var keyCode, shift, ctrl, alt;
14012         if(typeof key == "object" && !(key instanceof Array)){
14013             keyCode = key.key;
14014             shift = key.shift;
14015             ctrl = key.ctrl;
14016             alt = key.alt;
14017         }else{
14018             keyCode = key;
14019         }
14020         this.addBinding({
14021             key: keyCode,
14022             shift: shift,
14023             ctrl: ctrl,
14024             alt: alt,
14025             fn: fn,
14026             scope: scope
14027         })
14028     },
14029
14030     // private
14031     handleKeyDown : function(e){
14032             if(this.enabled){ //just in case
14033             var b = this.bindings;
14034             for(var i = 0, len = b.length; i < len; i++){
14035                 b[i].call(this, e);
14036             }
14037             }
14038         },
14039         
14040         /**
14041          * Returns true if this KeyMap is enabled
14042          * @return {Boolean} 
14043          */
14044         isEnabled : function(){
14045             return this.enabled;  
14046         },
14047         
14048         /**
14049          * Enables this KeyMap
14050          */
14051         enable: function(){
14052                 if(!this.enabled){
14053                     this.el.on(this.eventName, this.handleKeyDown, this);
14054                     this.enabled = true;
14055                 }
14056         },
14057
14058         /**
14059          * Disable this KeyMap
14060          */
14061         disable: function(){
14062                 if(this.enabled){
14063                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14064                     this.enabled = false;
14065                 }
14066         }
14067 };/*
14068  * Based on:
14069  * Ext JS Library 1.1.1
14070  * Copyright(c) 2006-2007, Ext JS, LLC.
14071  *
14072  * Originally Released Under LGPL - original licence link has changed is not relivant.
14073  *
14074  * Fork - LGPL
14075  * <script type="text/javascript">
14076  */
14077
14078  
14079 /**
14080  * @class Roo.util.TextMetrics
14081  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14082  * wide, in pixels, a given block of text will be.
14083  * @singleton
14084  */
14085 Roo.util.TextMetrics = function(){
14086     var shared;
14087     return {
14088         /**
14089          * Measures the size of the specified text
14090          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14091          * that can affect the size of the rendered text
14092          * @param {String} text The text to measure
14093          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14094          * in order to accurately measure the text height
14095          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14096          */
14097         measure : function(el, text, fixedWidth){
14098             if(!shared){
14099                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14100             }
14101             shared.bind(el);
14102             shared.setFixedWidth(fixedWidth || 'auto');
14103             return shared.getSize(text);
14104         },
14105
14106         /**
14107          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14108          * the overhead of multiple calls to initialize the style properties on each measurement.
14109          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14110          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14111          * in order to accurately measure the text height
14112          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14113          */
14114         createInstance : function(el, fixedWidth){
14115             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14116         }
14117     };
14118 }();
14119
14120  
14121
14122 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14123     var ml = new Roo.Element(document.createElement('div'));
14124     document.body.appendChild(ml.dom);
14125     ml.position('absolute');
14126     ml.setLeftTop(-1000, -1000);
14127     ml.hide();
14128
14129     if(fixedWidth){
14130         ml.setWidth(fixedWidth);
14131     }
14132      
14133     var instance = {
14134         /**
14135          * Returns the size of the specified text based on the internal element's style and width properties
14136          * @memberOf Roo.util.TextMetrics.Instance#
14137          * @param {String} text The text to measure
14138          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14139          */
14140         getSize : function(text){
14141             ml.update(text);
14142             var s = ml.getSize();
14143             ml.update('');
14144             return s;
14145         },
14146
14147         /**
14148          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14149          * that can affect the size of the rendered text
14150          * @memberOf Roo.util.TextMetrics.Instance#
14151          * @param {String/HTMLElement} el The element, dom node or id
14152          */
14153         bind : function(el){
14154             ml.setStyle(
14155                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14156             );
14157         },
14158
14159         /**
14160          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14161          * to set a fixed width in order to accurately measure the text height.
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {Number} width The width to set on the element
14164          */
14165         setFixedWidth : function(width){
14166             ml.setWidth(width);
14167         },
14168
14169         /**
14170          * Returns the measured width of the specified text
14171          * @memberOf Roo.util.TextMetrics.Instance#
14172          * @param {String} text The text to measure
14173          * @return {Number} width The width in pixels
14174          */
14175         getWidth : function(text){
14176             ml.dom.style.width = 'auto';
14177             return this.getSize(text).width;
14178         },
14179
14180         /**
14181          * Returns the measured height of the specified text.  For multiline text, be sure to call
14182          * {@link #setFixedWidth} if necessary.
14183          * @memberOf Roo.util.TextMetrics.Instance#
14184          * @param {String} text The text to measure
14185          * @return {Number} height The height in pixels
14186          */
14187         getHeight : function(text){
14188             return this.getSize(text).height;
14189         }
14190     };
14191
14192     instance.bind(bindTo);
14193
14194     return instance;
14195 };
14196
14197 // backwards compat
14198 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209 /**
14210  * @class Roo.state.Provider
14211  * Abstract base class for state provider implementations. This class provides methods
14212  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14213  * Provider interface.
14214  */
14215 Roo.state.Provider = function(){
14216     /**
14217      * @event statechange
14218      * Fires when a state change occurs.
14219      * @param {Provider} this This state provider
14220      * @param {String} key The state key which was changed
14221      * @param {String} value The encoded value for the state
14222      */
14223     this.addEvents({
14224         "statechange": true
14225     });
14226     this.state = {};
14227     Roo.state.Provider.superclass.constructor.call(this);
14228 };
14229 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14230     /**
14231      * Returns the current value for a key
14232      * @param {String} name The key name
14233      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14234      * @return {Mixed} The state data
14235      */
14236     get : function(name, defaultValue){
14237         return typeof this.state[name] == "undefined" ?
14238             defaultValue : this.state[name];
14239     },
14240     
14241     /**
14242      * Clears a value from the state
14243      * @param {String} name The key name
14244      */
14245     clear : function(name){
14246         delete this.state[name];
14247         this.fireEvent("statechange", this, name, null);
14248     },
14249     
14250     /**
14251      * Sets the value for a key
14252      * @param {String} name The key name
14253      * @param {Mixed} value The value to set
14254      */
14255     set : function(name, value){
14256         this.state[name] = value;
14257         this.fireEvent("statechange", this, name, value);
14258     },
14259     
14260     /**
14261      * Decodes a string previously encoded with {@link #encodeValue}.
14262      * @param {String} value The value to decode
14263      * @return {Mixed} The decoded value
14264      */
14265     decodeValue : function(cookie){
14266         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14267         var matches = re.exec(unescape(cookie));
14268         if(!matches || !matches[1]) return; // non state cookie
14269         var type = matches[1];
14270         var v = matches[2];
14271         switch(type){
14272             case "n":
14273                 return parseFloat(v);
14274             case "d":
14275                 return new Date(Date.parse(v));
14276             case "b":
14277                 return (v == "1");
14278             case "a":
14279                 var all = [];
14280                 var values = v.split("^");
14281                 for(var i = 0, len = values.length; i < len; i++){
14282                     all.push(this.decodeValue(values[i]));
14283                 }
14284                 return all;
14285            case "o":
14286                 var all = {};
14287                 var values = v.split("^");
14288                 for(var i = 0, len = values.length; i < len; i++){
14289                     var kv = values[i].split("=");
14290                     all[kv[0]] = this.decodeValue(kv[1]);
14291                 }
14292                 return all;
14293            default:
14294                 return v;
14295         }
14296     },
14297     
14298     /**
14299      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14300      * @param {Mixed} value The value to encode
14301      * @return {String} The encoded value
14302      */
14303     encodeValue : function(v){
14304         var enc;
14305         if(typeof v == "number"){
14306             enc = "n:" + v;
14307         }else if(typeof v == "boolean"){
14308             enc = "b:" + (v ? "1" : "0");
14309         }else if(v instanceof Date){
14310             enc = "d:" + v.toGMTString();
14311         }else if(v instanceof Array){
14312             var flat = "";
14313             for(var i = 0, len = v.length; i < len; i++){
14314                 flat += this.encodeValue(v[i]);
14315                 if(i != len-1) flat += "^";
14316             }
14317             enc = "a:" + flat;
14318         }else if(typeof v == "object"){
14319             var flat = "";
14320             for(var key in v){
14321                 if(typeof v[key] != "function"){
14322                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14323                 }
14324             }
14325             enc = "o:" + flat.substring(0, flat.length-1);
14326         }else{
14327             enc = "s:" + v;
14328         }
14329         return escape(enc);        
14330     }
14331 });
14332
14333 /*
14334  * Based on:
14335  * Ext JS Library 1.1.1
14336  * Copyright(c) 2006-2007, Ext JS, LLC.
14337  *
14338  * Originally Released Under LGPL - original licence link has changed is not relivant.
14339  *
14340  * Fork - LGPL
14341  * <script type="text/javascript">
14342  */
14343 /**
14344  * @class Roo.state.Manager
14345  * This is the global state manager. By default all components that are "state aware" check this class
14346  * for state information if you don't pass them a custom state provider. In order for this class
14347  * to be useful, it must be initialized with a provider when your application initializes.
14348  <pre><code>
14349 // in your initialization function
14350 init : function(){
14351    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14352    ...
14353    // supposed you have a {@link Roo.BorderLayout}
14354    var layout = new Roo.BorderLayout(...);
14355    layout.restoreState();
14356    // or a {Roo.BasicDialog}
14357    var dialog = new Roo.BasicDialog(...);
14358    dialog.restoreState();
14359  </code></pre>
14360  * @singleton
14361  */
14362 Roo.state.Manager = function(){
14363     var provider = new Roo.state.Provider();
14364     
14365     return {
14366         /**
14367          * Configures the default state provider for your application
14368          * @param {Provider} stateProvider The state provider to set
14369          */
14370         setProvider : function(stateProvider){
14371             provider = stateProvider;
14372         },
14373         
14374         /**
14375          * Returns the current value for a key
14376          * @param {String} name The key name
14377          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14378          * @return {Mixed} The state data
14379          */
14380         get : function(key, defaultValue){
14381             return provider.get(key, defaultValue);
14382         },
14383         
14384         /**
14385          * Sets the value for a key
14386          * @param {String} name The key name
14387          * @param {Mixed} value The state data
14388          */
14389          set : function(key, value){
14390             provider.set(key, value);
14391         },
14392         
14393         /**
14394          * Clears a value from the state
14395          * @param {String} name The key name
14396          */
14397         clear : function(key){
14398             provider.clear(key);
14399         },
14400         
14401         /**
14402          * Gets the currently configured state provider
14403          * @return {Provider} The state provider
14404          */
14405         getProvider : function(){
14406             return provider;
14407         }
14408     };
14409 }();
14410 /*
14411  * Based on:
14412  * Ext JS Library 1.1.1
14413  * Copyright(c) 2006-2007, Ext JS, LLC.
14414  *
14415  * Originally Released Under LGPL - original licence link has changed is not relivant.
14416  *
14417  * Fork - LGPL
14418  * <script type="text/javascript">
14419  */
14420 /**
14421  * @class Roo.state.CookieProvider
14422  * @extends Roo.state.Provider
14423  * The default Provider implementation which saves state via cookies.
14424  * <br />Usage:
14425  <pre><code>
14426    var cp = new Roo.state.CookieProvider({
14427        path: "/cgi-bin/",
14428        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14429        domain: "roojs.com"
14430    })
14431    Roo.state.Manager.setProvider(cp);
14432  </code></pre>
14433  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14434  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14435  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14436  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14437  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14438  * domain the page is running on including the 'www' like 'www.roojs.com')
14439  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14440  * @constructor
14441  * Create a new CookieProvider
14442  * @param {Object} config The configuration object
14443  */
14444 Roo.state.CookieProvider = function(config){
14445     Roo.state.CookieProvider.superclass.constructor.call(this);
14446     this.path = "/";
14447     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14448     this.domain = null;
14449     this.secure = false;
14450     Roo.apply(this, config);
14451     this.state = this.readCookies();
14452 };
14453
14454 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14455     // private
14456     set : function(name, value){
14457         if(typeof value == "undefined" || value === null){
14458             this.clear(name);
14459             return;
14460         }
14461         this.setCookie(name, value);
14462         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14463     },
14464
14465     // private
14466     clear : function(name){
14467         this.clearCookie(name);
14468         Roo.state.CookieProvider.superclass.clear.call(this, name);
14469     },
14470
14471     // private
14472     readCookies : function(){
14473         var cookies = {};
14474         var c = document.cookie + ";";
14475         var re = /\s?(.*?)=(.*?);/g;
14476         var matches;
14477         while((matches = re.exec(c)) != null){
14478             var name = matches[1];
14479             var value = matches[2];
14480             if(name && name.substring(0,3) == "ys-"){
14481                 cookies[name.substr(3)] = this.decodeValue(value);
14482             }
14483         }
14484         return cookies;
14485     },
14486
14487     // private
14488     setCookie : function(name, value){
14489         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14490            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14491            ((this.path == null) ? "" : ("; path=" + this.path)) +
14492            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14493            ((this.secure == true) ? "; secure" : "");
14494     },
14495
14496     // private
14497     clearCookie : function(name){
14498         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14499            ((this.path == null) ? "" : ("; path=" + this.path)) +
14500            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14501            ((this.secure == true) ? "; secure" : "");
14502     }
14503 });/*
14504  * Based on:
14505  * Ext JS Library 1.1.1
14506  * Copyright(c) 2006-2007, Ext JS, LLC.
14507  *
14508  * Originally Released Under LGPL - original licence link has changed is not relivant.
14509  *
14510  * Fork - LGPL
14511  * <script type="text/javascript">
14512  */
14513
14514
14515
14516 /*
14517  * These classes are derivatives of the similarly named classes in the YUI Library.
14518  * The original license:
14519  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14520  * Code licensed under the BSD License:
14521  * http://developer.yahoo.net/yui/license.txt
14522  */
14523
14524 (function() {
14525
14526 var Event=Roo.EventManager;
14527 var Dom=Roo.lib.Dom;
14528
14529 /**
14530  * @class Roo.dd.DragDrop
14531  * @extends Roo.util.Observable
14532  * Defines the interface and base operation of items that that can be
14533  * dragged or can be drop targets.  It was designed to be extended, overriding
14534  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14535  * Up to three html elements can be associated with a DragDrop instance:
14536  * <ul>
14537  * <li>linked element: the element that is passed into the constructor.
14538  * This is the element which defines the boundaries for interaction with
14539  * other DragDrop objects.</li>
14540  * <li>handle element(s): The drag operation only occurs if the element that
14541  * was clicked matches a handle element.  By default this is the linked
14542  * element, but there are times that you will want only a portion of the
14543  * linked element to initiate the drag operation, and the setHandleElId()
14544  * method provides a way to define this.</li>
14545  * <li>drag element: this represents the element that would be moved along
14546  * with the cursor during a drag operation.  By default, this is the linked
14547  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14548  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14549  * </li>
14550  * </ul>
14551  * This class should not be instantiated until the onload event to ensure that
14552  * the associated elements are available.
14553  * The following would define a DragDrop obj that would interact with any
14554  * other DragDrop obj in the "group1" group:
14555  * <pre>
14556  *  dd = new Roo.dd.DragDrop("div1", "group1");
14557  * </pre>
14558  * Since none of the event handlers have been implemented, nothing would
14559  * actually happen if you were to run the code above.  Normally you would
14560  * override this class or one of the default implementations, but you can
14561  * also override the methods you want on an instance of the class...
14562  * <pre>
14563  *  dd.onDragDrop = function(e, id) {
14564  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14565  *  }
14566  * </pre>
14567  * @constructor
14568  * @param {String} id of the element that is linked to this instance
14569  * @param {String} sGroup the group of related DragDrop objects
14570  * @param {object} config an object containing configurable attributes
14571  *                Valid properties for DragDrop:
14572  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14573  */
14574 Roo.dd.DragDrop = function(id, sGroup, config) {
14575     if (id) {
14576         this.init(id, sGroup, config);
14577     }
14578     
14579 };
14580
14581 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14582
14583     /**
14584      * The id of the element associated with this object.  This is what we
14585      * refer to as the "linked element" because the size and position of
14586      * this element is used to determine when the drag and drop objects have
14587      * interacted.
14588      * @property id
14589      * @type String
14590      */
14591     id: null,
14592
14593     /**
14594      * Configuration attributes passed into the constructor
14595      * @property config
14596      * @type object
14597      */
14598     config: null,
14599
14600     /**
14601      * The id of the element that will be dragged.  By default this is same
14602      * as the linked element , but could be changed to another element. Ex:
14603      * Roo.dd.DDProxy
14604      * @property dragElId
14605      * @type String
14606      * @private
14607      */
14608     dragElId: null,
14609
14610     /**
14611      * the id of the element that initiates the drag operation.  By default
14612      * this is the linked element, but could be changed to be a child of this
14613      * element.  This lets us do things like only starting the drag when the
14614      * header element within the linked html element is clicked.
14615      * @property handleElId
14616      * @type String
14617      * @private
14618      */
14619     handleElId: null,
14620
14621     /**
14622      * An associative array of HTML tags that will be ignored if clicked.
14623      * @property invalidHandleTypes
14624      * @type {string: string}
14625      */
14626     invalidHandleTypes: null,
14627
14628     /**
14629      * An associative array of ids for elements that will be ignored if clicked
14630      * @property invalidHandleIds
14631      * @type {string: string}
14632      */
14633     invalidHandleIds: null,
14634
14635     /**
14636      * An indexted array of css class names for elements that will be ignored
14637      * if clicked.
14638      * @property invalidHandleClasses
14639      * @type string[]
14640      */
14641     invalidHandleClasses: null,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageX
14647      * @type int
14648      * @private
14649      */
14650     startPageX: 0,
14651
14652     /**
14653      * The linked element's absolute X position at the time the drag was
14654      * started
14655      * @property startPageY
14656      * @type int
14657      * @private
14658      */
14659     startPageY: 0,
14660
14661     /**
14662      * The group defines a logical collection of DragDrop objects that are
14663      * related.  Instances only get events when interacting with other
14664      * DragDrop object in the same group.  This lets us define multiple
14665      * groups using a single DragDrop subclass if we want.
14666      * @property groups
14667      * @type {string: string}
14668      */
14669     groups: null,
14670
14671     /**
14672      * Individual drag/drop instances can be locked.  This will prevent
14673      * onmousedown start drag.
14674      * @property locked
14675      * @type boolean
14676      * @private
14677      */
14678     locked: false,
14679
14680     /**
14681      * Lock this instance
14682      * @method lock
14683      */
14684     lock: function() { this.locked = true; },
14685
14686     /**
14687      * Unlock this instace
14688      * @method unlock
14689      */
14690     unlock: function() { this.locked = false; },
14691
14692     /**
14693      * By default, all insances can be a drop target.  This can be disabled by
14694      * setting isTarget to false.
14695      * @method isTarget
14696      * @type boolean
14697      */
14698     isTarget: true,
14699
14700     /**
14701      * The padding configured for this drag and drop object for calculating
14702      * the drop zone intersection with this object.
14703      * @method padding
14704      * @type int[]
14705      */
14706     padding: null,
14707
14708     /**
14709      * Cached reference to the linked element
14710      * @property _domRef
14711      * @private
14712      */
14713     _domRef: null,
14714
14715     /**
14716      * Internal typeof flag
14717      * @property __ygDragDrop
14718      * @private
14719      */
14720     __ygDragDrop: true,
14721
14722     /**
14723      * Set to true when horizontal contraints are applied
14724      * @property constrainX
14725      * @type boolean
14726      * @private
14727      */
14728     constrainX: false,
14729
14730     /**
14731      * Set to true when vertical contraints are applied
14732      * @property constrainY
14733      * @type boolean
14734      * @private
14735      */
14736     constrainY: false,
14737
14738     /**
14739      * The left constraint
14740      * @property minX
14741      * @type int
14742      * @private
14743      */
14744     minX: 0,
14745
14746     /**
14747      * The right constraint
14748      * @property maxX
14749      * @type int
14750      * @private
14751      */
14752     maxX: 0,
14753
14754     /**
14755      * The up constraint
14756      * @property minY
14757      * @type int
14758      * @type int
14759      * @private
14760      */
14761     minY: 0,
14762
14763     /**
14764      * The down constraint
14765      * @property maxY
14766      * @type int
14767      * @private
14768      */
14769     maxY: 0,
14770
14771     /**
14772      * Maintain offsets when we resetconstraints.  Set to true when you want
14773      * the position of the element relative to its parent to stay the same
14774      * when the page changes
14775      *
14776      * @property maintainOffset
14777      * @type boolean
14778      */
14779     maintainOffset: false,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * horizontal graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property xTicks
14786      * @type int[]
14787      */
14788     xTicks: null,
14789
14790     /**
14791      * Array of pixel locations the element will snap to if we specified a
14792      * vertical graduation/interval.  This array is generated automatically
14793      * when you define a tick interval.
14794      * @property yTicks
14795      * @type int[]
14796      */
14797     yTicks: null,
14798
14799     /**
14800      * By default the drag and drop instance will only respond to the primary
14801      * button click (left button for a right-handed mouse).  Set to true to
14802      * allow drag and drop to start with any mouse click that is propogated
14803      * by the browser
14804      * @property primaryButtonOnly
14805      * @type boolean
14806      */
14807     primaryButtonOnly: true,
14808
14809     /**
14810      * The availabe property is false until the linked dom element is accessible.
14811      * @property available
14812      * @type boolean
14813      */
14814     available: false,
14815
14816     /**
14817      * By default, drags can only be initiated if the mousedown occurs in the
14818      * region the linked element is.  This is done in part to work around a
14819      * bug in some browsers that mis-report the mousedown if the previous
14820      * mouseup happened outside of the window.  This property is set to true
14821      * if outer handles are defined.
14822      *
14823      * @property hasOuterHandles
14824      * @type boolean
14825      * @default false
14826      */
14827     hasOuterHandles: false,
14828
14829     /**
14830      * Code that executes immediately before the startDrag event
14831      * @method b4StartDrag
14832      * @private
14833      */
14834     b4StartDrag: function(x, y) { },
14835
14836     /**
14837      * Abstract method called after a drag/drop object is clicked
14838      * and the drag or mousedown time thresholds have beeen met.
14839      * @method startDrag
14840      * @param {int} X click location
14841      * @param {int} Y click location
14842      */
14843     startDrag: function(x, y) { /* override this */ },
14844
14845     /**
14846      * Code that executes immediately before the onDrag event
14847      * @method b4Drag
14848      * @private
14849      */
14850     b4Drag: function(e) { },
14851
14852     /**
14853      * Abstract method called during the onMouseMove event while dragging an
14854      * object.
14855      * @method onDrag
14856      * @param {Event} e the mousemove event
14857      */
14858     onDrag: function(e) { /* override this */ },
14859
14860     /**
14861      * Abstract method called when this element fist begins hovering over
14862      * another DragDrop obj
14863      * @method onDragEnter
14864      * @param {Event} e the mousemove event
14865      * @param {String|DragDrop[]} id In POINT mode, the element
14866      * id this is hovering over.  In INTERSECT mode, an array of one or more
14867      * dragdrop items being hovered over.
14868      */
14869     onDragEnter: function(e, id) { /* override this */ },
14870
14871     /**
14872      * Code that executes immediately before the onDragOver event
14873      * @method b4DragOver
14874      * @private
14875      */
14876     b4DragOver: function(e) { },
14877
14878     /**
14879      * Abstract method called when this element is hovering over another
14880      * DragDrop obj
14881      * @method onDragOver
14882      * @param {Event} e the mousemove event
14883      * @param {String|DragDrop[]} id In POINT mode, the element
14884      * id this is hovering over.  In INTERSECT mode, an array of dd items
14885      * being hovered over.
14886      */
14887     onDragOver: function(e, id) { /* override this */ },
14888
14889     /**
14890      * Code that executes immediately before the onDragOut event
14891      * @method b4DragOut
14892      * @private
14893      */
14894     b4DragOut: function(e) { },
14895
14896     /**
14897      * Abstract method called when we are no longer hovering over an element
14898      * @method onDragOut
14899      * @param {Event} e the mousemove event
14900      * @param {String|DragDrop[]} id In POINT mode, the element
14901      * id this was hovering over.  In INTERSECT mode, an array of dd items
14902      * that the mouse is no longer over.
14903      */
14904     onDragOut: function(e, id) { /* override this */ },
14905
14906     /**
14907      * Code that executes immediately before the onDragDrop event
14908      * @method b4DragDrop
14909      * @private
14910      */
14911     b4DragDrop: function(e) { },
14912
14913     /**
14914      * Abstract method called when this item is dropped on another DragDrop
14915      * obj
14916      * @method onDragDrop
14917      * @param {Event} e the mouseup event
14918      * @param {String|DragDrop[]} id In POINT mode, the element
14919      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14920      * was dropped on.
14921      */
14922     onDragDrop: function(e, id) { /* override this */ },
14923
14924     /**
14925      * Abstract method called when this item is dropped on an area with no
14926      * drop target
14927      * @method onInvalidDrop
14928      * @param {Event} e the mouseup event
14929      */
14930     onInvalidDrop: function(e) { /* override this */ },
14931
14932     /**
14933      * Code that executes immediately before the endDrag event
14934      * @method b4EndDrag
14935      * @private
14936      */
14937     b4EndDrag: function(e) { },
14938
14939     /**
14940      * Fired when we are done dragging the object
14941      * @method endDrag
14942      * @param {Event} e the mouseup event
14943      */
14944     endDrag: function(e) { /* override this */ },
14945
14946     /**
14947      * Code executed immediately before the onMouseDown event
14948      * @method b4MouseDown
14949      * @param {Event} e the mousedown event
14950      * @private
14951      */
14952     b4MouseDown: function(e) {  },
14953
14954     /**
14955      * Event handler that fires when a drag/drop obj gets a mousedown
14956      * @method onMouseDown
14957      * @param {Event} e the mousedown event
14958      */
14959     onMouseDown: function(e) { /* override this */ },
14960
14961     /**
14962      * Event handler that fires when a drag/drop obj gets a mouseup
14963      * @method onMouseUp
14964      * @param {Event} e the mouseup event
14965      */
14966     onMouseUp: function(e) { /* override this */ },
14967
14968     /**
14969      * Override the onAvailable method to do what is needed after the initial
14970      * position was determined.
14971      * @method onAvailable
14972      */
14973     onAvailable: function () {
14974     },
14975
14976     /*
14977      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14978      * @type Object
14979      */
14980     defaultPadding : {left:0, right:0, top:0, bottom:0},
14981
14982     /*
14983      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14984  *
14985  * Usage:
14986  <pre><code>
14987  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14988                 { dragElId: "existingProxyDiv" });
14989  dd.startDrag = function(){
14990      this.constrainTo("parent-id");
14991  };
14992  </code></pre>
14993  * Or you can initalize it using the {@link Roo.Element} object:
14994  <pre><code>
14995  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14996      startDrag : function(){
14997          this.constrainTo("parent-id");
14998      }
14999  });
15000  </code></pre>
15001      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15002      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15003      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15004      * an object containing the sides to pad. For example: {right:10, bottom:10}
15005      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15006      */
15007     constrainTo : function(constrainTo, pad, inContent){
15008         if(typeof pad == "number"){
15009             pad = {left: pad, right:pad, top:pad, bottom:pad};
15010         }
15011         pad = pad || this.defaultPadding;
15012         var b = Roo.get(this.getEl()).getBox();
15013         var ce = Roo.get(constrainTo);
15014         var s = ce.getScroll();
15015         var c, cd = ce.dom;
15016         if(cd == document.body){
15017             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15018         }else{
15019             xy = ce.getXY();
15020             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15021         }
15022
15023
15024         var topSpace = b.y - c.y;
15025         var leftSpace = b.x - c.x;
15026
15027         this.resetConstraints();
15028         this.setXConstraint(leftSpace - (pad.left||0), // left
15029                 c.width - leftSpace - b.width - (pad.right||0) //right
15030         );
15031         this.setYConstraint(topSpace - (pad.top||0), //top
15032                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15033         );
15034     },
15035
15036     /**
15037      * Returns a reference to the linked element
15038      * @method getEl
15039      * @return {HTMLElement} the html element
15040      */
15041     getEl: function() {
15042         if (!this._domRef) {
15043             this._domRef = Roo.getDom(this.id);
15044         }
15045
15046         return this._domRef;
15047     },
15048
15049     /**
15050      * Returns a reference to the actual element to drag.  By default this is
15051      * the same as the html element, but it can be assigned to another
15052      * element. An example of this can be found in Roo.dd.DDProxy
15053      * @method getDragEl
15054      * @return {HTMLElement} the html element
15055      */
15056     getDragEl: function() {
15057         return Roo.getDom(this.dragElId);
15058     },
15059
15060     /**
15061      * Sets up the DragDrop object.  Must be called in the constructor of any
15062      * Roo.dd.DragDrop subclass
15063      * @method init
15064      * @param id the id of the linked element
15065      * @param {String} sGroup the group of related items
15066      * @param {object} config configuration attributes
15067      */
15068     init: function(id, sGroup, config) {
15069         this.initTarget(id, sGroup, config);
15070         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15071         // Event.on(this.id, "selectstart", Event.preventDefault);
15072     },
15073
15074     /**
15075      * Initializes Targeting functionality only... the object does not
15076      * get a mousedown handler.
15077      * @method initTarget
15078      * @param id the id of the linked element
15079      * @param {String} sGroup the group of related items
15080      * @param {object} config configuration attributes
15081      */
15082     initTarget: function(id, sGroup, config) {
15083
15084         // configuration attributes
15085         this.config = config || {};
15086
15087         // create a local reference to the drag and drop manager
15088         this.DDM = Roo.dd.DDM;
15089         // initialize the groups array
15090         this.groups = {};
15091
15092         // assume that we have an element reference instead of an id if the
15093         // parameter is not a string
15094         if (typeof id !== "string") {
15095             id = Roo.id(id);
15096         }
15097
15098         // set the id
15099         this.id = id;
15100
15101         // add to an interaction group
15102         this.addToGroup((sGroup) ? sGroup : "default");
15103
15104         // We don't want to register this as the handle with the manager
15105         // so we just set the id rather than calling the setter.
15106         this.handleElId = id;
15107
15108         // the linked element is the element that gets dragged by default
15109         this.setDragElId(id);
15110
15111         // by default, clicked anchors will not start drag operations.
15112         this.invalidHandleTypes = { A: "A" };
15113         this.invalidHandleIds = {};
15114         this.invalidHandleClasses = [];
15115
15116         this.applyConfig();
15117
15118         this.handleOnAvailable();
15119     },
15120
15121     /**
15122      * Applies the configuration parameters that were passed into the constructor.
15123      * This is supposed to happen at each level through the inheritance chain.  So
15124      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15125      * DragDrop in order to get all of the parameters that are available in
15126      * each object.
15127      * @method applyConfig
15128      */
15129     applyConfig: function() {
15130
15131         // configurable properties:
15132         //    padding, isTarget, maintainOffset, primaryButtonOnly
15133         this.padding           = this.config.padding || [0, 0, 0, 0];
15134         this.isTarget          = (this.config.isTarget !== false);
15135         this.maintainOffset    = (this.config.maintainOffset);
15136         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15137
15138     },
15139
15140     /**
15141      * Executed when the linked element is available
15142      * @method handleOnAvailable
15143      * @private
15144      */
15145     handleOnAvailable: function() {
15146         this.available = true;
15147         this.resetConstraints();
15148         this.onAvailable();
15149     },
15150
15151      /**
15152      * Configures the padding for the target zone in px.  Effectively expands
15153      * (or reduces) the virtual object size for targeting calculations.
15154      * Supports css-style shorthand; if only one parameter is passed, all sides
15155      * will have that padding, and if only two are passed, the top and bottom
15156      * will have the first param, the left and right the second.
15157      * @method setPadding
15158      * @param {int} iTop    Top pad
15159      * @param {int} iRight  Right pad
15160      * @param {int} iBot    Bot pad
15161      * @param {int} iLeft   Left pad
15162      */
15163     setPadding: function(iTop, iRight, iBot, iLeft) {
15164         // this.padding = [iLeft, iRight, iTop, iBot];
15165         if (!iRight && 0 !== iRight) {
15166             this.padding = [iTop, iTop, iTop, iTop];
15167         } else if (!iBot && 0 !== iBot) {
15168             this.padding = [iTop, iRight, iTop, iRight];
15169         } else {
15170             this.padding = [iTop, iRight, iBot, iLeft];
15171         }
15172     },
15173
15174     /**
15175      * Stores the initial placement of the linked element.
15176      * @method setInitialPosition
15177      * @param {int} diffX   the X offset, default 0
15178      * @param {int} diffY   the Y offset, default 0
15179      */
15180     setInitPosition: function(diffX, diffY) {
15181         var el = this.getEl();
15182
15183         if (!this.DDM.verifyEl(el)) {
15184             return;
15185         }
15186
15187         var dx = diffX || 0;
15188         var dy = diffY || 0;
15189
15190         var p = Dom.getXY( el );
15191
15192         this.initPageX = p[0] - dx;
15193         this.initPageY = p[1] - dy;
15194
15195         this.lastPageX = p[0];
15196         this.lastPageY = p[1];
15197
15198
15199         this.setStartPosition(p);
15200     },
15201
15202     /**
15203      * Sets the start position of the element.  This is set when the obj
15204      * is initialized, the reset when a drag is started.
15205      * @method setStartPosition
15206      * @param pos current position (from previous lookup)
15207      * @private
15208      */
15209     setStartPosition: function(pos) {
15210         var p = pos || Dom.getXY( this.getEl() );
15211         this.deltaSetXY = null;
15212
15213         this.startPageX = p[0];
15214         this.startPageY = p[1];
15215     },
15216
15217     /**
15218      * Add this instance to a group of related drag/drop objects.  All
15219      * instances belong to at least one group, and can belong to as many
15220      * groups as needed.
15221      * @method addToGroup
15222      * @param sGroup {string} the name of the group
15223      */
15224     addToGroup: function(sGroup) {
15225         this.groups[sGroup] = true;
15226         this.DDM.regDragDrop(this, sGroup);
15227     },
15228
15229     /**
15230      * Remove's this instance from the supplied interaction group
15231      * @method removeFromGroup
15232      * @param {string}  sGroup  The group to drop
15233      */
15234     removeFromGroup: function(sGroup) {
15235         if (this.groups[sGroup]) {
15236             delete this.groups[sGroup];
15237         }
15238
15239         this.DDM.removeDDFromGroup(this, sGroup);
15240     },
15241
15242     /**
15243      * Allows you to specify that an element other than the linked element
15244      * will be moved with the cursor during a drag
15245      * @method setDragElId
15246      * @param id {string} the id of the element that will be used to initiate the drag
15247      */
15248     setDragElId: function(id) {
15249         this.dragElId = id;
15250     },
15251
15252     /**
15253      * Allows you to specify a child of the linked element that should be
15254      * used to initiate the drag operation.  An example of this would be if
15255      * you have a content div with text and links.  Clicking anywhere in the
15256      * content area would normally start the drag operation.  Use this method
15257      * to specify that an element inside of the content div is the element
15258      * that starts the drag operation.
15259      * @method setHandleElId
15260      * @param id {string} the id of the element that will be used to
15261      * initiate the drag.
15262      */
15263     setHandleElId: function(id) {
15264         if (typeof id !== "string") {
15265             id = Roo.id(id);
15266         }
15267         this.handleElId = id;
15268         this.DDM.regHandle(this.id, id);
15269     },
15270
15271     /**
15272      * Allows you to set an element outside of the linked element as a drag
15273      * handle
15274      * @method setOuterHandleElId
15275      * @param id the id of the element that will be used to initiate the drag
15276      */
15277     setOuterHandleElId: function(id) {
15278         if (typeof id !== "string") {
15279             id = Roo.id(id);
15280         }
15281         Event.on(id, "mousedown",
15282                 this.handleMouseDown, this);
15283         this.setHandleElId(id);
15284
15285         this.hasOuterHandles = true;
15286     },
15287
15288     /**
15289      * Remove all drag and drop hooks for this element
15290      * @method unreg
15291      */
15292     unreg: function() {
15293         Event.un(this.id, "mousedown",
15294                 this.handleMouseDown);
15295         this._domRef = null;
15296         this.DDM._remove(this);
15297     },
15298
15299     destroy : function(){
15300         this.unreg();
15301     },
15302
15303     /**
15304      * Returns true if this instance is locked, or the drag drop mgr is locked
15305      * (meaning that all drag/drop is disabled on the page.)
15306      * @method isLocked
15307      * @return {boolean} true if this obj or all drag/drop is locked, else
15308      * false
15309      */
15310     isLocked: function() {
15311         return (this.DDM.isLocked() || this.locked);
15312     },
15313
15314     /**
15315      * Fired when this object is clicked
15316      * @method handleMouseDown
15317      * @param {Event} e
15318      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15319      * @private
15320      */
15321     handleMouseDown: function(e, oDD){
15322         if (this.primaryButtonOnly && e.button != 0) {
15323             return;
15324         }
15325
15326         if (this.isLocked()) {
15327             return;
15328         }
15329
15330         this.DDM.refreshCache(this.groups);
15331
15332         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15333         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15334         } else {
15335             if (this.clickValidator(e)) {
15336
15337                 // set the initial element position
15338                 this.setStartPosition();
15339
15340
15341                 this.b4MouseDown(e);
15342                 this.onMouseDown(e);
15343
15344                 this.DDM.handleMouseDown(e, this);
15345
15346                 this.DDM.stopEvent(e);
15347             } else {
15348
15349
15350             }
15351         }
15352     },
15353
15354     clickValidator: function(e) {
15355         var target = e.getTarget();
15356         return ( this.isValidHandleChild(target) &&
15357                     (this.id == this.handleElId ||
15358                         this.DDM.handleWasClicked(target, this.id)) );
15359     },
15360
15361     /**
15362      * Allows you to specify a tag name that should not start a drag operation
15363      * when clicked.  This is designed to facilitate embedding links within a
15364      * drag handle that do something other than start the drag.
15365      * @method addInvalidHandleType
15366      * @param {string} tagName the type of element to exclude
15367      */
15368     addInvalidHandleType: function(tagName) {
15369         var type = tagName.toUpperCase();
15370         this.invalidHandleTypes[type] = type;
15371     },
15372
15373     /**
15374      * Lets you to specify an element id for a child of a drag handle
15375      * that should not initiate a drag
15376      * @method addInvalidHandleId
15377      * @param {string} id the element id of the element you wish to ignore
15378      */
15379     addInvalidHandleId: function(id) {
15380         if (typeof id !== "string") {
15381             id = Roo.id(id);
15382         }
15383         this.invalidHandleIds[id] = id;
15384     },
15385
15386     /**
15387      * Lets you specify a css class of elements that will not initiate a drag
15388      * @method addInvalidHandleClass
15389      * @param {string} cssClass the class of the elements you wish to ignore
15390      */
15391     addInvalidHandleClass: function(cssClass) {
15392         this.invalidHandleClasses.push(cssClass);
15393     },
15394
15395     /**
15396      * Unsets an excluded tag name set by addInvalidHandleType
15397      * @method removeInvalidHandleType
15398      * @param {string} tagName the type of element to unexclude
15399      */
15400     removeInvalidHandleType: function(tagName) {
15401         var type = tagName.toUpperCase();
15402         // this.invalidHandleTypes[type] = null;
15403         delete this.invalidHandleTypes[type];
15404     },
15405
15406     /**
15407      * Unsets an invalid handle id
15408      * @method removeInvalidHandleId
15409      * @param {string} id the id of the element to re-enable
15410      */
15411     removeInvalidHandleId: function(id) {
15412         if (typeof id !== "string") {
15413             id = Roo.id(id);
15414         }
15415         delete this.invalidHandleIds[id];
15416     },
15417
15418     /**
15419      * Unsets an invalid css class
15420      * @method removeInvalidHandleClass
15421      * @param {string} cssClass the class of the element(s) you wish to
15422      * re-enable
15423      */
15424     removeInvalidHandleClass: function(cssClass) {
15425         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15426             if (this.invalidHandleClasses[i] == cssClass) {
15427                 delete this.invalidHandleClasses[i];
15428             }
15429         }
15430     },
15431
15432     /**
15433      * Checks the tag exclusion list to see if this click should be ignored
15434      * @method isValidHandleChild
15435      * @param {HTMLElement} node the HTMLElement to evaluate
15436      * @return {boolean} true if this is a valid tag type, false if not
15437      */
15438     isValidHandleChild: function(node) {
15439
15440         var valid = true;
15441         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15442         var nodeName;
15443         try {
15444             nodeName = node.nodeName.toUpperCase();
15445         } catch(e) {
15446             nodeName = node.nodeName;
15447         }
15448         valid = valid && !this.invalidHandleTypes[nodeName];
15449         valid = valid && !this.invalidHandleIds[node.id];
15450
15451         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15452             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15453         }
15454
15455
15456         return valid;
15457
15458     },
15459
15460     /**
15461      * Create the array of horizontal tick marks if an interval was specified
15462      * in setXConstraint().
15463      * @method setXTicks
15464      * @private
15465      */
15466     setXTicks: function(iStartX, iTickSize) {
15467         this.xTicks = [];
15468         this.xTickSize = iTickSize;
15469
15470         var tickMap = {};
15471
15472         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15473             if (!tickMap[i]) {
15474                 this.xTicks[this.xTicks.length] = i;
15475                 tickMap[i] = true;
15476             }
15477         }
15478
15479         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15480             if (!tickMap[i]) {
15481                 this.xTicks[this.xTicks.length] = i;
15482                 tickMap[i] = true;
15483             }
15484         }
15485
15486         this.xTicks.sort(this.DDM.numericSort) ;
15487     },
15488
15489     /**
15490      * Create the array of vertical tick marks if an interval was specified in
15491      * setYConstraint().
15492      * @method setYTicks
15493      * @private
15494      */
15495     setYTicks: function(iStartY, iTickSize) {
15496         this.yTicks = [];
15497         this.yTickSize = iTickSize;
15498
15499         var tickMap = {};
15500
15501         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15502             if (!tickMap[i]) {
15503                 this.yTicks[this.yTicks.length] = i;
15504                 tickMap[i] = true;
15505             }
15506         }
15507
15508         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15509             if (!tickMap[i]) {
15510                 this.yTicks[this.yTicks.length] = i;
15511                 tickMap[i] = true;
15512             }
15513         }
15514
15515         this.yTicks.sort(this.DDM.numericSort) ;
15516     },
15517
15518     /**
15519      * By default, the element can be dragged any place on the screen.  Use
15520      * this method to limit the horizontal travel of the element.  Pass in
15521      * 0,0 for the parameters if you want to lock the drag to the y axis.
15522      * @method setXConstraint
15523      * @param {int} iLeft the number of pixels the element can move to the left
15524      * @param {int} iRight the number of pixels the element can move to the
15525      * right
15526      * @param {int} iTickSize optional parameter for specifying that the
15527      * element
15528      * should move iTickSize pixels at a time.
15529      */
15530     setXConstraint: function(iLeft, iRight, iTickSize) {
15531         this.leftConstraint = iLeft;
15532         this.rightConstraint = iRight;
15533
15534         this.minX = this.initPageX - iLeft;
15535         this.maxX = this.initPageX + iRight;
15536         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15537
15538         this.constrainX = true;
15539     },
15540
15541     /**
15542      * Clears any constraints applied to this instance.  Also clears ticks
15543      * since they can't exist independent of a constraint at this time.
15544      * @method clearConstraints
15545      */
15546     clearConstraints: function() {
15547         this.constrainX = false;
15548         this.constrainY = false;
15549         this.clearTicks();
15550     },
15551
15552     /**
15553      * Clears any tick interval defined for this instance
15554      * @method clearTicks
15555      */
15556     clearTicks: function() {
15557         this.xTicks = null;
15558         this.yTicks = null;
15559         this.xTickSize = 0;
15560         this.yTickSize = 0;
15561     },
15562
15563     /**
15564      * By default, the element can be dragged any place on the screen.  Set
15565      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15566      * parameters if you want to lock the drag to the x axis.
15567      * @method setYConstraint
15568      * @param {int} iUp the number of pixels the element can move up
15569      * @param {int} iDown the number of pixels the element can move down
15570      * @param {int} iTickSize optional parameter for specifying that the
15571      * element should move iTickSize pixels at a time.
15572      */
15573     setYConstraint: function(iUp, iDown, iTickSize) {
15574         this.topConstraint = iUp;
15575         this.bottomConstraint = iDown;
15576
15577         this.minY = this.initPageY - iUp;
15578         this.maxY = this.initPageY + iDown;
15579         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15580
15581         this.constrainY = true;
15582
15583     },
15584
15585     /**
15586      * resetConstraints must be called if you manually reposition a dd element.
15587      * @method resetConstraints
15588      * @param {boolean} maintainOffset
15589      */
15590     resetConstraints: function() {
15591
15592
15593         // Maintain offsets if necessary
15594         if (this.initPageX || this.initPageX === 0) {
15595             // figure out how much this thing has moved
15596             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15597             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15598
15599             this.setInitPosition(dx, dy);
15600
15601         // This is the first time we have detected the element's position
15602         } else {
15603             this.setInitPosition();
15604         }
15605
15606         if (this.constrainX) {
15607             this.setXConstraint( this.leftConstraint,
15608                                  this.rightConstraint,
15609                                  this.xTickSize        );
15610         }
15611
15612         if (this.constrainY) {
15613             this.setYConstraint( this.topConstraint,
15614                                  this.bottomConstraint,
15615                                  this.yTickSize         );
15616         }
15617     },
15618
15619     /**
15620      * Normally the drag element is moved pixel by pixel, but we can specify
15621      * that it move a number of pixels at a time.  This method resolves the
15622      * location when we have it set up like this.
15623      * @method getTick
15624      * @param {int} val where we want to place the object
15625      * @param {int[]} tickArray sorted array of valid points
15626      * @return {int} the closest tick
15627      * @private
15628      */
15629     getTick: function(val, tickArray) {
15630
15631         if (!tickArray) {
15632             // If tick interval is not defined, it is effectively 1 pixel,
15633             // so we return the value passed to us.
15634             return val;
15635         } else if (tickArray[0] >= val) {
15636             // The value is lower than the first tick, so we return the first
15637             // tick.
15638             return tickArray[0];
15639         } else {
15640             for (var i=0, len=tickArray.length; i<len; ++i) {
15641                 var next = i + 1;
15642                 if (tickArray[next] && tickArray[next] >= val) {
15643                     var diff1 = val - tickArray[i];
15644                     var diff2 = tickArray[next] - val;
15645                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15646                 }
15647             }
15648
15649             // The value is larger than the last tick, so we return the last
15650             // tick.
15651             return tickArray[tickArray.length - 1];
15652         }
15653     },
15654
15655     /**
15656      * toString method
15657      * @method toString
15658      * @return {string} string representation of the dd obj
15659      */
15660     toString: function() {
15661         return ("DragDrop " + this.id);
15662     }
15663
15664 });
15665
15666 })();
15667 /*
15668  * Based on:
15669  * Ext JS Library 1.1.1
15670  * Copyright(c) 2006-2007, Ext JS, LLC.
15671  *
15672  * Originally Released Under LGPL - original licence link has changed is not relivant.
15673  *
15674  * Fork - LGPL
15675  * <script type="text/javascript">
15676  */
15677
15678
15679 /**
15680  * The drag and drop utility provides a framework for building drag and drop
15681  * applications.  In addition to enabling drag and drop for specific elements,
15682  * the drag and drop elements are tracked by the manager class, and the
15683  * interactions between the various elements are tracked during the drag and
15684  * the implementing code is notified about these important moments.
15685  */
15686
15687 // Only load the library once.  Rewriting the manager class would orphan
15688 // existing drag and drop instances.
15689 if (!Roo.dd.DragDropMgr) {
15690
15691 /**
15692  * @class Roo.dd.DragDropMgr
15693  * DragDropMgr is a singleton that tracks the element interaction for
15694  * all DragDrop items in the window.  Generally, you will not call
15695  * this class directly, but it does have helper methods that could
15696  * be useful in your DragDrop implementations.
15697  * @singleton
15698  */
15699 Roo.dd.DragDropMgr = function() {
15700
15701     var Event = Roo.EventManager;
15702
15703     return {
15704
15705         /**
15706          * Two dimensional Array of registered DragDrop objects.  The first
15707          * dimension is the DragDrop item group, the second the DragDrop
15708          * object.
15709          * @property ids
15710          * @type {string: string}
15711          * @private
15712          * @static
15713          */
15714         ids: {},
15715
15716         /**
15717          * Array of element ids defined as drag handles.  Used to determine
15718          * if the element that generated the mousedown event is actually the
15719          * handle and not the html element itself.
15720          * @property handleIds
15721          * @type {string: string}
15722          * @private
15723          * @static
15724          */
15725         handleIds: {},
15726
15727         /**
15728          * the DragDrop object that is currently being dragged
15729          * @property dragCurrent
15730          * @type DragDrop
15731          * @private
15732          * @static
15733          **/
15734         dragCurrent: null,
15735
15736         /**
15737          * the DragDrop object(s) that are being hovered over
15738          * @property dragOvers
15739          * @type Array
15740          * @private
15741          * @static
15742          */
15743         dragOvers: {},
15744
15745         /**
15746          * the X distance between the cursor and the object being dragged
15747          * @property deltaX
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaX: 0,
15753
15754         /**
15755          * the Y distance between the cursor and the object being dragged
15756          * @property deltaY
15757          * @type int
15758          * @private
15759          * @static
15760          */
15761         deltaY: 0,
15762
15763         /**
15764          * Flag to determine if we should prevent the default behavior of the
15765          * events we define. By default this is true, but this can be set to
15766          * false if you need the default behavior (not recommended)
15767          * @property preventDefault
15768          * @type boolean
15769          * @static
15770          */
15771         preventDefault: true,
15772
15773         /**
15774          * Flag to determine if we should stop the propagation of the events
15775          * we generate. This is true by default but you may want to set it to
15776          * false if the html element contains other features that require the
15777          * mouse click.
15778          * @property stopPropagation
15779          * @type boolean
15780          * @static
15781          */
15782         stopPropagation: true,
15783
15784         /**
15785          * Internal flag that is set to true when drag and drop has been
15786          * intialized
15787          * @property initialized
15788          * @private
15789          * @static
15790          */
15791         initalized: false,
15792
15793         /**
15794          * All drag and drop can be disabled.
15795          * @property locked
15796          * @private
15797          * @static
15798          */
15799         locked: false,
15800
15801         /**
15802          * Called the first time an element is registered.
15803          * @method init
15804          * @private
15805          * @static
15806          */
15807         init: function() {
15808             this.initialized = true;
15809         },
15810
15811         /**
15812          * In point mode, drag and drop interaction is defined by the
15813          * location of the cursor during the drag/drop
15814          * @property POINT
15815          * @type int
15816          * @static
15817          */
15818         POINT: 0,
15819
15820         /**
15821          * In intersect mode, drag and drop interactio nis defined by the
15822          * overlap of two or more drag and drop objects.
15823          * @property INTERSECT
15824          * @type int
15825          * @static
15826          */
15827         INTERSECT: 1,
15828
15829         /**
15830          * The current drag and drop mode.  Default: POINT
15831          * @property mode
15832          * @type int
15833          * @static
15834          */
15835         mode: 0,
15836
15837         /**
15838          * Runs method on all drag and drop objects
15839          * @method _execOnAll
15840          * @private
15841          * @static
15842          */
15843         _execOnAll: function(sMethod, args) {
15844             for (var i in this.ids) {
15845                 for (var j in this.ids[i]) {
15846                     var oDD = this.ids[i][j];
15847                     if (! this.isTypeOfDD(oDD)) {
15848                         continue;
15849                     }
15850                     oDD[sMethod].apply(oDD, args);
15851                 }
15852             }
15853         },
15854
15855         /**
15856          * Drag and drop initialization.  Sets up the global event handlers
15857          * @method _onLoad
15858          * @private
15859          * @static
15860          */
15861         _onLoad: function() {
15862
15863             this.init();
15864
15865
15866             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15867             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15868             Event.on(window,   "unload",    this._onUnload, this, true);
15869             Event.on(window,   "resize",    this._onResize, this, true);
15870             // Event.on(window,   "mouseout",    this._test);
15871
15872         },
15873
15874         /**
15875          * Reset constraints on all drag and drop objs
15876          * @method _onResize
15877          * @private
15878          * @static
15879          */
15880         _onResize: function(e) {
15881             this._execOnAll("resetConstraints", []);
15882         },
15883
15884         /**
15885          * Lock all drag and drop functionality
15886          * @method lock
15887          * @static
15888          */
15889         lock: function() { this.locked = true; },
15890
15891         /**
15892          * Unlock all drag and drop functionality
15893          * @method unlock
15894          * @static
15895          */
15896         unlock: function() { this.locked = false; },
15897
15898         /**
15899          * Is drag and drop locked?
15900          * @method isLocked
15901          * @return {boolean} True if drag and drop is locked, false otherwise.
15902          * @static
15903          */
15904         isLocked: function() { return this.locked; },
15905
15906         /**
15907          * Location cache that is set for all drag drop objects when a drag is
15908          * initiated, cleared when the drag is finished.
15909          * @property locationCache
15910          * @private
15911          * @static
15912          */
15913         locationCache: {},
15914
15915         /**
15916          * Set useCache to false if you want to force object the lookup of each
15917          * drag and drop linked element constantly during a drag.
15918          * @property useCache
15919          * @type boolean
15920          * @static
15921          */
15922         useCache: true,
15923
15924         /**
15925          * The number of pixels that the mouse needs to move after the
15926          * mousedown before the drag is initiated.  Default=3;
15927          * @property clickPixelThresh
15928          * @type int
15929          * @static
15930          */
15931         clickPixelThresh: 3,
15932
15933         /**
15934          * The number of milliseconds after the mousedown event to initiate the
15935          * drag if we don't get a mouseup event. Default=1000
15936          * @property clickTimeThresh
15937          * @type int
15938          * @static
15939          */
15940         clickTimeThresh: 350,
15941
15942         /**
15943          * Flag that indicates that either the drag pixel threshold or the
15944          * mousdown time threshold has been met
15945          * @property dragThreshMet
15946          * @type boolean
15947          * @private
15948          * @static
15949          */
15950         dragThreshMet: false,
15951
15952         /**
15953          * Timeout used for the click time threshold
15954          * @property clickTimeout
15955          * @type Object
15956          * @private
15957          * @static
15958          */
15959         clickTimeout: null,
15960
15961         /**
15962          * The X position of the mousedown event stored for later use when a
15963          * drag threshold is met.
15964          * @property startX
15965          * @type int
15966          * @private
15967          * @static
15968          */
15969         startX: 0,
15970
15971         /**
15972          * The Y position of the mousedown event stored for later use when a
15973          * drag threshold is met.
15974          * @property startY
15975          * @type int
15976          * @private
15977          * @static
15978          */
15979         startY: 0,
15980
15981         /**
15982          * Each DragDrop instance must be registered with the DragDropMgr.
15983          * This is executed in DragDrop.init()
15984          * @method regDragDrop
15985          * @param {DragDrop} oDD the DragDrop object to register
15986          * @param {String} sGroup the name of the group this element belongs to
15987          * @static
15988          */
15989         regDragDrop: function(oDD, sGroup) {
15990             if (!this.initialized) { this.init(); }
15991
15992             if (!this.ids[sGroup]) {
15993                 this.ids[sGroup] = {};
15994             }
15995             this.ids[sGroup][oDD.id] = oDD;
15996         },
15997
15998         /**
15999          * Removes the supplied dd instance from the supplied group. Executed
16000          * by DragDrop.removeFromGroup, so don't call this function directly.
16001          * @method removeDDFromGroup
16002          * @private
16003          * @static
16004          */
16005         removeDDFromGroup: function(oDD, sGroup) {
16006             if (!this.ids[sGroup]) {
16007                 this.ids[sGroup] = {};
16008             }
16009
16010             var obj = this.ids[sGroup];
16011             if (obj && obj[oDD.id]) {
16012                 delete obj[oDD.id];
16013             }
16014         },
16015
16016         /**
16017          * Unregisters a drag and drop item.  This is executed in
16018          * DragDrop.unreg, use that method instead of calling this directly.
16019          * @method _remove
16020          * @private
16021          * @static
16022          */
16023         _remove: function(oDD) {
16024             for (var g in oDD.groups) {
16025                 if (g && this.ids[g][oDD.id]) {
16026                     delete this.ids[g][oDD.id];
16027                 }
16028             }
16029             delete this.handleIds[oDD.id];
16030         },
16031
16032         /**
16033          * Each DragDrop handle element must be registered.  This is done
16034          * automatically when executing DragDrop.setHandleElId()
16035          * @method regHandle
16036          * @param {String} sDDId the DragDrop id this element is a handle for
16037          * @param {String} sHandleId the id of the element that is the drag
16038          * handle
16039          * @static
16040          */
16041         regHandle: function(sDDId, sHandleId) {
16042             if (!this.handleIds[sDDId]) {
16043                 this.handleIds[sDDId] = {};
16044             }
16045             this.handleIds[sDDId][sHandleId] = sHandleId;
16046         },
16047
16048         /**
16049          * Utility function to determine if a given element has been
16050          * registered as a drag drop item.
16051          * @method isDragDrop
16052          * @param {String} id the element id to check
16053          * @return {boolean} true if this element is a DragDrop item,
16054          * false otherwise
16055          * @static
16056          */
16057         isDragDrop: function(id) {
16058             return ( this.getDDById(id) ) ? true : false;
16059         },
16060
16061         /**
16062          * Returns the drag and drop instances that are in all groups the
16063          * passed in instance belongs to.
16064          * @method getRelated
16065          * @param {DragDrop} p_oDD the obj to get related data for
16066          * @param {boolean} bTargetsOnly if true, only return targetable objs
16067          * @return {DragDrop[]} the related instances
16068          * @static
16069          */
16070         getRelated: function(p_oDD, bTargetsOnly) {
16071             var oDDs = [];
16072             for (var i in p_oDD.groups) {
16073                 for (j in this.ids[i]) {
16074                     var dd = this.ids[i][j];
16075                     if (! this.isTypeOfDD(dd)) {
16076                         continue;
16077                     }
16078                     if (!bTargetsOnly || dd.isTarget) {
16079                         oDDs[oDDs.length] = dd;
16080                     }
16081                 }
16082             }
16083
16084             return oDDs;
16085         },
16086
16087         /**
16088          * Returns true if the specified dd target is a legal target for
16089          * the specifice drag obj
16090          * @method isLegalTarget
16091          * @param {DragDrop} the drag obj
16092          * @param {DragDrop} the target
16093          * @return {boolean} true if the target is a legal target for the
16094          * dd obj
16095          * @static
16096          */
16097         isLegalTarget: function (oDD, oTargetDD) {
16098             var targets = this.getRelated(oDD, true);
16099             for (var i=0, len=targets.length;i<len;++i) {
16100                 if (targets[i].id == oTargetDD.id) {
16101                     return true;
16102                 }
16103             }
16104
16105             return false;
16106         },
16107
16108         /**
16109          * My goal is to be able to transparently determine if an object is
16110          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16111          * returns "object", oDD.constructor.toString() always returns
16112          * "DragDrop" and not the name of the subclass.  So for now it just
16113          * evaluates a well-known variable in DragDrop.
16114          * @method isTypeOfDD
16115          * @param {Object} the object to evaluate
16116          * @return {boolean} true if typeof oDD = DragDrop
16117          * @static
16118          */
16119         isTypeOfDD: function (oDD) {
16120             return (oDD && oDD.__ygDragDrop);
16121         },
16122
16123         /**
16124          * Utility function to determine if a given element has been
16125          * registered as a drag drop handle for the given Drag Drop object.
16126          * @method isHandle
16127          * @param {String} id the element id to check
16128          * @return {boolean} true if this element is a DragDrop handle, false
16129          * otherwise
16130          * @static
16131          */
16132         isHandle: function(sDDId, sHandleId) {
16133             return ( this.handleIds[sDDId] &&
16134                             this.handleIds[sDDId][sHandleId] );
16135         },
16136
16137         /**
16138          * Returns the DragDrop instance for a given id
16139          * @method getDDById
16140          * @param {String} id the id of the DragDrop object
16141          * @return {DragDrop} the drag drop object, null if it is not found
16142          * @static
16143          */
16144         getDDById: function(id) {
16145             for (var i in this.ids) {
16146                 if (this.ids[i][id]) {
16147                     return this.ids[i][id];
16148                 }
16149             }
16150             return null;
16151         },
16152
16153         /**
16154          * Fired after a registered DragDrop object gets the mousedown event.
16155          * Sets up the events required to track the object being dragged
16156          * @method handleMouseDown
16157          * @param {Event} e the event
16158          * @param oDD the DragDrop object being dragged
16159          * @private
16160          * @static
16161          */
16162         handleMouseDown: function(e, oDD) {
16163             if(Roo.QuickTips){
16164                 Roo.QuickTips.disable();
16165             }
16166             this.currentTarget = e.getTarget();
16167
16168             this.dragCurrent = oDD;
16169
16170             var el = oDD.getEl();
16171
16172             // track start position
16173             this.startX = e.getPageX();
16174             this.startY = e.getPageY();
16175
16176             this.deltaX = this.startX - el.offsetLeft;
16177             this.deltaY = this.startY - el.offsetTop;
16178
16179             this.dragThreshMet = false;
16180
16181             this.clickTimeout = setTimeout(
16182                     function() {
16183                         var DDM = Roo.dd.DDM;
16184                         DDM.startDrag(DDM.startX, DDM.startY);
16185                     },
16186                     this.clickTimeThresh );
16187         },
16188
16189         /**
16190          * Fired when either the drag pixel threshol or the mousedown hold
16191          * time threshold has been met.
16192          * @method startDrag
16193          * @param x {int} the X position of the original mousedown
16194          * @param y {int} the Y position of the original mousedown
16195          * @static
16196          */
16197         startDrag: function(x, y) {
16198             clearTimeout(this.clickTimeout);
16199             if (this.dragCurrent) {
16200                 this.dragCurrent.b4StartDrag(x, y);
16201                 this.dragCurrent.startDrag(x, y);
16202             }
16203             this.dragThreshMet = true;
16204         },
16205
16206         /**
16207          * Internal function to handle the mouseup event.  Will be invoked
16208          * from the context of the document.
16209          * @method handleMouseUp
16210          * @param {Event} e the event
16211          * @private
16212          * @static
16213          */
16214         handleMouseUp: function(e) {
16215
16216             if(Roo.QuickTips){
16217                 Roo.QuickTips.enable();
16218             }
16219             if (! this.dragCurrent) {
16220                 return;
16221             }
16222
16223             clearTimeout(this.clickTimeout);
16224
16225             if (this.dragThreshMet) {
16226                 this.fireEvents(e, true);
16227             } else {
16228             }
16229
16230             this.stopDrag(e);
16231
16232             this.stopEvent(e);
16233         },
16234
16235         /**
16236          * Utility to stop event propagation and event default, if these
16237          * features are turned on.
16238          * @method stopEvent
16239          * @param {Event} e the event as returned by this.getEvent()
16240          * @static
16241          */
16242         stopEvent: function(e){
16243             if(this.stopPropagation) {
16244                 e.stopPropagation();
16245             }
16246
16247             if (this.preventDefault) {
16248                 e.preventDefault();
16249             }
16250         },
16251
16252         /**
16253          * Internal function to clean up event handlers after the drag
16254          * operation is complete
16255          * @method stopDrag
16256          * @param {Event} e the event
16257          * @private
16258          * @static
16259          */
16260         stopDrag: function(e) {
16261             // Fire the drag end event for the item that was dragged
16262             if (this.dragCurrent) {
16263                 if (this.dragThreshMet) {
16264                     this.dragCurrent.b4EndDrag(e);
16265                     this.dragCurrent.endDrag(e);
16266                 }
16267
16268                 this.dragCurrent.onMouseUp(e);
16269             }
16270
16271             this.dragCurrent = null;
16272             this.dragOvers = {};
16273         },
16274
16275         /**
16276          * Internal function to handle the mousemove event.  Will be invoked
16277          * from the context of the html element.
16278          *
16279          * @TODO figure out what we can do about mouse events lost when the
16280          * user drags objects beyond the window boundary.  Currently we can
16281          * detect this in internet explorer by verifying that the mouse is
16282          * down during the mousemove event.  Firefox doesn't give us the
16283          * button state on the mousemove event.
16284          * @method handleMouseMove
16285          * @param {Event} e the event
16286          * @private
16287          * @static
16288          */
16289         handleMouseMove: function(e) {
16290             if (! this.dragCurrent) {
16291                 return true;
16292             }
16293
16294             // var button = e.which || e.button;
16295
16296             // check for IE mouseup outside of page boundary
16297             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16298                 this.stopEvent(e);
16299                 return this.handleMouseUp(e);
16300             }
16301
16302             if (!this.dragThreshMet) {
16303                 var diffX = Math.abs(this.startX - e.getPageX());
16304                 var diffY = Math.abs(this.startY - e.getPageY());
16305                 if (diffX > this.clickPixelThresh ||
16306                             diffY > this.clickPixelThresh) {
16307                     this.startDrag(this.startX, this.startY);
16308                 }
16309             }
16310
16311             if (this.dragThreshMet) {
16312                 this.dragCurrent.b4Drag(e);
16313                 this.dragCurrent.onDrag(e);
16314                 if(!this.dragCurrent.moveOnly){
16315                     this.fireEvents(e, false);
16316                 }
16317             }
16318
16319             this.stopEvent(e);
16320
16321             return true;
16322         },
16323
16324         /**
16325          * Iterates over all of the DragDrop elements to find ones we are
16326          * hovering over or dropping on
16327          * @method fireEvents
16328          * @param {Event} e the event
16329          * @param {boolean} isDrop is this a drop op or a mouseover op?
16330          * @private
16331          * @static
16332          */
16333         fireEvents: function(e, isDrop) {
16334             var dc = this.dragCurrent;
16335
16336             // If the user did the mouse up outside of the window, we could
16337             // get here even though we have ended the drag.
16338             if (!dc || dc.isLocked()) {
16339                 return;
16340             }
16341
16342             var pt = e.getPoint();
16343
16344             // cache the previous dragOver array
16345             var oldOvers = [];
16346
16347             var outEvts   = [];
16348             var overEvts  = [];
16349             var dropEvts  = [];
16350             var enterEvts = [];
16351
16352             // Check to see if the object(s) we were hovering over is no longer
16353             // being hovered over so we can fire the onDragOut event
16354             for (var i in this.dragOvers) {
16355
16356                 var ddo = this.dragOvers[i];
16357
16358                 if (! this.isTypeOfDD(ddo)) {
16359                     continue;
16360                 }
16361
16362                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16363                     outEvts.push( ddo );
16364                 }
16365
16366                 oldOvers[i] = true;
16367                 delete this.dragOvers[i];
16368             }
16369
16370             for (var sGroup in dc.groups) {
16371
16372                 if ("string" != typeof sGroup) {
16373                     continue;
16374                 }
16375
16376                 for (i in this.ids[sGroup]) {
16377                     var oDD = this.ids[sGroup][i];
16378                     if (! this.isTypeOfDD(oDD)) {
16379                         continue;
16380                     }
16381
16382                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16383                         if (this.isOverTarget(pt, oDD, this.mode)) {
16384                             // look for drop interactions
16385                             if (isDrop) {
16386                                 dropEvts.push( oDD );
16387                             // look for drag enter and drag over interactions
16388                             } else {
16389
16390                                 // initial drag over: dragEnter fires
16391                                 if (!oldOvers[oDD.id]) {
16392                                     enterEvts.push( oDD );
16393                                 // subsequent drag overs: dragOver fires
16394                                 } else {
16395                                     overEvts.push( oDD );
16396                                 }
16397
16398                                 this.dragOvers[oDD.id] = oDD;
16399                             }
16400                         }
16401                     }
16402                 }
16403             }
16404
16405             if (this.mode) {
16406                 if (outEvts.length) {
16407                     dc.b4DragOut(e, outEvts);
16408                     dc.onDragOut(e, outEvts);
16409                 }
16410
16411                 if (enterEvts.length) {
16412                     dc.onDragEnter(e, enterEvts);
16413                 }
16414
16415                 if (overEvts.length) {
16416                     dc.b4DragOver(e, overEvts);
16417                     dc.onDragOver(e, overEvts);
16418                 }
16419
16420                 if (dropEvts.length) {
16421                     dc.b4DragDrop(e, dropEvts);
16422                     dc.onDragDrop(e, dropEvts);
16423                 }
16424
16425             } else {
16426                 // fire dragout events
16427                 var len = 0;
16428                 for (i=0, len=outEvts.length; i<len; ++i) {
16429                     dc.b4DragOut(e, outEvts[i].id);
16430                     dc.onDragOut(e, outEvts[i].id);
16431                 }
16432
16433                 // fire enter events
16434                 for (i=0,len=enterEvts.length; i<len; ++i) {
16435                     // dc.b4DragEnter(e, oDD.id);
16436                     dc.onDragEnter(e, enterEvts[i].id);
16437                 }
16438
16439                 // fire over events
16440                 for (i=0,len=overEvts.length; i<len; ++i) {
16441                     dc.b4DragOver(e, overEvts[i].id);
16442                     dc.onDragOver(e, overEvts[i].id);
16443                 }
16444
16445                 // fire drop events
16446                 for (i=0, len=dropEvts.length; i<len; ++i) {
16447                     dc.b4DragDrop(e, dropEvts[i].id);
16448                     dc.onDragDrop(e, dropEvts[i].id);
16449                 }
16450
16451             }
16452
16453             // notify about a drop that did not find a target
16454             if (isDrop && !dropEvts.length) {
16455                 dc.onInvalidDrop(e);
16456             }
16457
16458         },
16459
16460         /**
16461          * Helper function for getting the best match from the list of drag
16462          * and drop objects returned by the drag and drop events when we are
16463          * in INTERSECT mode.  It returns either the first object that the
16464          * cursor is over, or the object that has the greatest overlap with
16465          * the dragged element.
16466          * @method getBestMatch
16467          * @param  {DragDrop[]} dds The array of drag and drop objects
16468          * targeted
16469          * @return {DragDrop}       The best single match
16470          * @static
16471          */
16472         getBestMatch: function(dds) {
16473             var winner = null;
16474             // Return null if the input is not what we expect
16475             //if (!dds || !dds.length || dds.length == 0) {
16476                // winner = null;
16477             // If there is only one item, it wins
16478             //} else if (dds.length == 1) {
16479
16480             var len = dds.length;
16481
16482             if (len == 1) {
16483                 winner = dds[0];
16484             } else {
16485                 // Loop through the targeted items
16486                 for (var i=0; i<len; ++i) {
16487                     var dd = dds[i];
16488                     // If the cursor is over the object, it wins.  If the
16489                     // cursor is over multiple matches, the first one we come
16490                     // to wins.
16491                     if (dd.cursorIsOver) {
16492                         winner = dd;
16493                         break;
16494                     // Otherwise the object with the most overlap wins
16495                     } else {
16496                         if (!winner ||
16497                             winner.overlap.getArea() < dd.overlap.getArea()) {
16498                             winner = dd;
16499                         }
16500                     }
16501                 }
16502             }
16503
16504             return winner;
16505         },
16506
16507         /**
16508          * Refreshes the cache of the top-left and bottom-right points of the
16509          * drag and drop objects in the specified group(s).  This is in the
16510          * format that is stored in the drag and drop instance, so typical
16511          * usage is:
16512          * <code>
16513          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16514          * </code>
16515          * Alternatively:
16516          * <code>
16517          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16518          * </code>
16519          * @TODO this really should be an indexed array.  Alternatively this
16520          * method could accept both.
16521          * @method refreshCache
16522          * @param {Object} groups an associative array of groups to refresh
16523          * @static
16524          */
16525         refreshCache: function(groups) {
16526             for (var sGroup in groups) {
16527                 if ("string" != typeof sGroup) {
16528                     continue;
16529                 }
16530                 for (var i in this.ids[sGroup]) {
16531                     var oDD = this.ids[sGroup][i];
16532
16533                     if (this.isTypeOfDD(oDD)) {
16534                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16535                         var loc = this.getLocation(oDD);
16536                         if (loc) {
16537                             this.locationCache[oDD.id] = loc;
16538                         } else {
16539                             delete this.locationCache[oDD.id];
16540                             // this will unregister the drag and drop object if
16541                             // the element is not in a usable state
16542                             // oDD.unreg();
16543                         }
16544                     }
16545                 }
16546             }
16547         },
16548
16549         /**
16550          * This checks to make sure an element exists and is in the DOM.  The
16551          * main purpose is to handle cases where innerHTML is used to remove
16552          * drag and drop objects from the DOM.  IE provides an 'unspecified
16553          * error' when trying to access the offsetParent of such an element
16554          * @method verifyEl
16555          * @param {HTMLElement} el the element to check
16556          * @return {boolean} true if the element looks usable
16557          * @static
16558          */
16559         verifyEl: function(el) {
16560             if (el) {
16561                 var parent;
16562                 if(Roo.isIE){
16563                     try{
16564                         parent = el.offsetParent;
16565                     }catch(e){}
16566                 }else{
16567                     parent = el.offsetParent;
16568                 }
16569                 if (parent) {
16570                     return true;
16571                 }
16572             }
16573
16574             return false;
16575         },
16576
16577         /**
16578          * Returns a Region object containing the drag and drop element's position
16579          * and size, including the padding configured for it
16580          * @method getLocation
16581          * @param {DragDrop} oDD the drag and drop object to get the
16582          *                       location for
16583          * @return {Roo.lib.Region} a Region object representing the total area
16584          *                             the element occupies, including any padding
16585          *                             the instance is configured for.
16586          * @static
16587          */
16588         getLocation: function(oDD) {
16589             if (! this.isTypeOfDD(oDD)) {
16590                 return null;
16591             }
16592
16593             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16594
16595             try {
16596                 pos= Roo.lib.Dom.getXY(el);
16597             } catch (e) { }
16598
16599             if (!pos) {
16600                 return null;
16601             }
16602
16603             x1 = pos[0];
16604             x2 = x1 + el.offsetWidth;
16605             y1 = pos[1];
16606             y2 = y1 + el.offsetHeight;
16607
16608             t = y1 - oDD.padding[0];
16609             r = x2 + oDD.padding[1];
16610             b = y2 + oDD.padding[2];
16611             l = x1 - oDD.padding[3];
16612
16613             return new Roo.lib.Region( t, r, b, l );
16614         },
16615
16616         /**
16617          * Checks the cursor location to see if it over the target
16618          * @method isOverTarget
16619          * @param {Roo.lib.Point} pt The point to evaluate
16620          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16621          * @return {boolean} true if the mouse is over the target
16622          * @private
16623          * @static
16624          */
16625         isOverTarget: function(pt, oTarget, intersect) {
16626             // use cache if available
16627             var loc = this.locationCache[oTarget.id];
16628             if (!loc || !this.useCache) {
16629                 loc = this.getLocation(oTarget);
16630                 this.locationCache[oTarget.id] = loc;
16631
16632             }
16633
16634             if (!loc) {
16635                 return false;
16636             }
16637
16638             oTarget.cursorIsOver = loc.contains( pt );
16639
16640             // DragDrop is using this as a sanity check for the initial mousedown
16641             // in this case we are done.  In POINT mode, if the drag obj has no
16642             // contraints, we are also done. Otherwise we need to evaluate the
16643             // location of the target as related to the actual location of the
16644             // dragged element.
16645             var dc = this.dragCurrent;
16646             if (!dc || !dc.getTargetCoord ||
16647                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16648                 return oTarget.cursorIsOver;
16649             }
16650
16651             oTarget.overlap = null;
16652
16653             // Get the current location of the drag element, this is the
16654             // location of the mouse event less the delta that represents
16655             // where the original mousedown happened on the element.  We
16656             // need to consider constraints and ticks as well.
16657             var pos = dc.getTargetCoord(pt.x, pt.y);
16658
16659             var el = dc.getDragEl();
16660             var curRegion = new Roo.lib.Region( pos.y,
16661                                                    pos.x + el.offsetWidth,
16662                                                    pos.y + el.offsetHeight,
16663                                                    pos.x );
16664
16665             var overlap = curRegion.intersect(loc);
16666
16667             if (overlap) {
16668                 oTarget.overlap = overlap;
16669                 return (intersect) ? true : oTarget.cursorIsOver;
16670             } else {
16671                 return false;
16672             }
16673         },
16674
16675         /**
16676          * unload event handler
16677          * @method _onUnload
16678          * @private
16679          * @static
16680          */
16681         _onUnload: function(e, me) {
16682             Roo.dd.DragDropMgr.unregAll();
16683         },
16684
16685         /**
16686          * Cleans up the drag and drop events and objects.
16687          * @method unregAll
16688          * @private
16689          * @static
16690          */
16691         unregAll: function() {
16692
16693             if (this.dragCurrent) {
16694                 this.stopDrag();
16695                 this.dragCurrent = null;
16696             }
16697
16698             this._execOnAll("unreg", []);
16699
16700             for (i in this.elementCache) {
16701                 delete this.elementCache[i];
16702             }
16703
16704             this.elementCache = {};
16705             this.ids = {};
16706         },
16707
16708         /**
16709          * A cache of DOM elements
16710          * @property elementCache
16711          * @private
16712          * @static
16713          */
16714         elementCache: {},
16715
16716         /**
16717          * Get the wrapper for the DOM element specified
16718          * @method getElWrapper
16719          * @param {String} id the id of the element to get
16720          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16721          * @private
16722          * @deprecated This wrapper isn't that useful
16723          * @static
16724          */
16725         getElWrapper: function(id) {
16726             var oWrapper = this.elementCache[id];
16727             if (!oWrapper || !oWrapper.el) {
16728                 oWrapper = this.elementCache[id] =
16729                     new this.ElementWrapper(Roo.getDom(id));
16730             }
16731             return oWrapper;
16732         },
16733
16734         /**
16735          * Returns the actual DOM element
16736          * @method getElement
16737          * @param {String} id the id of the elment to get
16738          * @return {Object} The element
16739          * @deprecated use Roo.getDom instead
16740          * @static
16741          */
16742         getElement: function(id) {
16743             return Roo.getDom(id);
16744         },
16745
16746         /**
16747          * Returns the style property for the DOM element (i.e.,
16748          * document.getElById(id).style)
16749          * @method getCss
16750          * @param {String} id the id of the elment to get
16751          * @return {Object} The style property of the element
16752          * @deprecated use Roo.getDom instead
16753          * @static
16754          */
16755         getCss: function(id) {
16756             var el = Roo.getDom(id);
16757             return (el) ? el.style : null;
16758         },
16759
16760         /**
16761          * Inner class for cached elements
16762          * @class DragDropMgr.ElementWrapper
16763          * @for DragDropMgr
16764          * @private
16765          * @deprecated
16766          */
16767         ElementWrapper: function(el) {
16768                 /**
16769                  * The element
16770                  * @property el
16771                  */
16772                 this.el = el || null;
16773                 /**
16774                  * The element id
16775                  * @property id
16776                  */
16777                 this.id = this.el && el.id;
16778                 /**
16779                  * A reference to the style property
16780                  * @property css
16781                  */
16782                 this.css = this.el && el.style;
16783             },
16784
16785         /**
16786          * Returns the X position of an html element
16787          * @method getPosX
16788          * @param el the element for which to get the position
16789          * @return {int} the X coordinate
16790          * @for DragDropMgr
16791          * @deprecated use Roo.lib.Dom.getX instead
16792          * @static
16793          */
16794         getPosX: function(el) {
16795             return Roo.lib.Dom.getX(el);
16796         },
16797
16798         /**
16799          * Returns the Y position of an html element
16800          * @method getPosY
16801          * @param el the element for which to get the position
16802          * @return {int} the Y coordinate
16803          * @deprecated use Roo.lib.Dom.getY instead
16804          * @static
16805          */
16806         getPosY: function(el) {
16807             return Roo.lib.Dom.getY(el);
16808         },
16809
16810         /**
16811          * Swap two nodes.  In IE, we use the native method, for others we
16812          * emulate the IE behavior
16813          * @method swapNode
16814          * @param n1 the first node to swap
16815          * @param n2 the other node to swap
16816          * @static
16817          */
16818         swapNode: function(n1, n2) {
16819             if (n1.swapNode) {
16820                 n1.swapNode(n2);
16821             } else {
16822                 var p = n2.parentNode;
16823                 var s = n2.nextSibling;
16824
16825                 if (s == n1) {
16826                     p.insertBefore(n1, n2);
16827                 } else if (n2 == n1.nextSibling) {
16828                     p.insertBefore(n2, n1);
16829                 } else {
16830                     n1.parentNode.replaceChild(n2, n1);
16831                     p.insertBefore(n1, s);
16832                 }
16833             }
16834         },
16835
16836         /**
16837          * Returns the current scroll position
16838          * @method getScroll
16839          * @private
16840          * @static
16841          */
16842         getScroll: function () {
16843             var t, l, dde=document.documentElement, db=document.body;
16844             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16845                 t = dde.scrollTop;
16846                 l = dde.scrollLeft;
16847             } else if (db) {
16848                 t = db.scrollTop;
16849                 l = db.scrollLeft;
16850             } else {
16851
16852             }
16853             return { top: t, left: l };
16854         },
16855
16856         /**
16857          * Returns the specified element style property
16858          * @method getStyle
16859          * @param {HTMLElement} el          the element
16860          * @param {string}      styleProp   the style property
16861          * @return {string} The value of the style property
16862          * @deprecated use Roo.lib.Dom.getStyle
16863          * @static
16864          */
16865         getStyle: function(el, styleProp) {
16866             return Roo.fly(el).getStyle(styleProp);
16867         },
16868
16869         /**
16870          * Gets the scrollTop
16871          * @method getScrollTop
16872          * @return {int} the document's scrollTop
16873          * @static
16874          */
16875         getScrollTop: function () { return this.getScroll().top; },
16876
16877         /**
16878          * Gets the scrollLeft
16879          * @method getScrollLeft
16880          * @return {int} the document's scrollTop
16881          * @static
16882          */
16883         getScrollLeft: function () { return this.getScroll().left; },
16884
16885         /**
16886          * Sets the x/y position of an element to the location of the
16887          * target element.
16888          * @method moveToEl
16889          * @param {HTMLElement} moveEl      The element to move
16890          * @param {HTMLElement} targetEl    The position reference element
16891          * @static
16892          */
16893         moveToEl: function (moveEl, targetEl) {
16894             var aCoord = Roo.lib.Dom.getXY(targetEl);
16895             Roo.lib.Dom.setXY(moveEl, aCoord);
16896         },
16897
16898         /**
16899          * Numeric array sort function
16900          * @method numericSort
16901          * @static
16902          */
16903         numericSort: function(a, b) { return (a - b); },
16904
16905         /**
16906          * Internal counter
16907          * @property _timeoutCount
16908          * @private
16909          * @static
16910          */
16911         _timeoutCount: 0,
16912
16913         /**
16914          * Trying to make the load order less important.  Without this we get
16915          * an error if this file is loaded before the Event Utility.
16916          * @method _addListeners
16917          * @private
16918          * @static
16919          */
16920         _addListeners: function() {
16921             var DDM = Roo.dd.DDM;
16922             if ( Roo.lib.Event && document ) {
16923                 DDM._onLoad();
16924             } else {
16925                 if (DDM._timeoutCount > 2000) {
16926                 } else {
16927                     setTimeout(DDM._addListeners, 10);
16928                     if (document && document.body) {
16929                         DDM._timeoutCount += 1;
16930                     }
16931                 }
16932             }
16933         },
16934
16935         /**
16936          * Recursively searches the immediate parent and all child nodes for
16937          * the handle element in order to determine wheter or not it was
16938          * clicked.
16939          * @method handleWasClicked
16940          * @param node the html element to inspect
16941          * @static
16942          */
16943         handleWasClicked: function(node, id) {
16944             if (this.isHandle(id, node.id)) {
16945                 return true;
16946             } else {
16947                 // check to see if this is a text node child of the one we want
16948                 var p = node.parentNode;
16949
16950                 while (p) {
16951                     if (this.isHandle(id, p.id)) {
16952                         return true;
16953                     } else {
16954                         p = p.parentNode;
16955                     }
16956                 }
16957             }
16958
16959             return false;
16960         }
16961
16962     };
16963
16964 }();
16965
16966 // shorter alias, save a few bytes
16967 Roo.dd.DDM = Roo.dd.DragDropMgr;
16968 Roo.dd.DDM._addListeners();
16969
16970 }/*
16971  * Based on:
16972  * Ext JS Library 1.1.1
16973  * Copyright(c) 2006-2007, Ext JS, LLC.
16974  *
16975  * Originally Released Under LGPL - original licence link has changed is not relivant.
16976  *
16977  * Fork - LGPL
16978  * <script type="text/javascript">
16979  */
16980
16981 /**
16982  * @class Roo.dd.DD
16983  * A DragDrop implementation where the linked element follows the
16984  * mouse cursor during a drag.
16985  * @extends Roo.dd.DragDrop
16986  * @constructor
16987  * @param {String} id the id of the linked element
16988  * @param {String} sGroup the group of related DragDrop items
16989  * @param {object} config an object containing configurable attributes
16990  *                Valid properties for DD:
16991  *                    scroll
16992  */
16993 Roo.dd.DD = function(id, sGroup, config) {
16994     if (id) {
16995         this.init(id, sGroup, config);
16996     }
16997 };
16998
16999 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17000
17001     /**
17002      * When set to true, the utility automatically tries to scroll the browser
17003      * window wehn a drag and drop element is dragged near the viewport boundary.
17004      * Defaults to true.
17005      * @property scroll
17006      * @type boolean
17007      */
17008     scroll: true,
17009
17010     /**
17011      * Sets the pointer offset to the distance between the linked element's top
17012      * left corner and the location the element was clicked
17013      * @method autoOffset
17014      * @param {int} iPageX the X coordinate of the click
17015      * @param {int} iPageY the Y coordinate of the click
17016      */
17017     autoOffset: function(iPageX, iPageY) {
17018         var x = iPageX - this.startPageX;
17019         var y = iPageY - this.startPageY;
17020         this.setDelta(x, y);
17021     },
17022
17023     /**
17024      * Sets the pointer offset.  You can call this directly to force the
17025      * offset to be in a particular location (e.g., pass in 0,0 to set it
17026      * to the center of the object)
17027      * @method setDelta
17028      * @param {int} iDeltaX the distance from the left
17029      * @param {int} iDeltaY the distance from the top
17030      */
17031     setDelta: function(iDeltaX, iDeltaY) {
17032         this.deltaX = iDeltaX;
17033         this.deltaY = iDeltaY;
17034     },
17035
17036     /**
17037      * Sets the drag element to the location of the mousedown or click event,
17038      * maintaining the cursor location relative to the location on the element
17039      * that was clicked.  Override this if you want to place the element in a
17040      * location other than where the cursor is.
17041      * @method setDragElPos
17042      * @param {int} iPageX the X coordinate of the mousedown or drag event
17043      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17044      */
17045     setDragElPos: function(iPageX, iPageY) {
17046         // the first time we do this, we are going to check to make sure
17047         // the element has css positioning
17048
17049         var el = this.getDragEl();
17050         this.alignElWithMouse(el, iPageX, iPageY);
17051     },
17052
17053     /**
17054      * Sets the element to the location of the mousedown or click event,
17055      * maintaining the cursor location relative to the location on the element
17056      * that was clicked.  Override this if you want to place the element in a
17057      * location other than where the cursor is.
17058      * @method alignElWithMouse
17059      * @param {HTMLElement} el the element to move
17060      * @param {int} iPageX the X coordinate of the mousedown or drag event
17061      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17062      */
17063     alignElWithMouse: function(el, iPageX, iPageY) {
17064         var oCoord = this.getTargetCoord(iPageX, iPageY);
17065         var fly = el.dom ? el : Roo.fly(el);
17066         if (!this.deltaSetXY) {
17067             var aCoord = [oCoord.x, oCoord.y];
17068             fly.setXY(aCoord);
17069             var newLeft = fly.getLeft(true);
17070             var newTop  = fly.getTop(true);
17071             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17072         } else {
17073             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17074         }
17075
17076         this.cachePosition(oCoord.x, oCoord.y);
17077         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17078         return oCoord;
17079     },
17080
17081     /**
17082      * Saves the most recent position so that we can reset the constraints and
17083      * tick marks on-demand.  We need to know this so that we can calculate the
17084      * number of pixels the element is offset from its original position.
17085      * @method cachePosition
17086      * @param iPageX the current x position (optional, this just makes it so we
17087      * don't have to look it up again)
17088      * @param iPageY the current y position (optional, this just makes it so we
17089      * don't have to look it up again)
17090      */
17091     cachePosition: function(iPageX, iPageY) {
17092         if (iPageX) {
17093             this.lastPageX = iPageX;
17094             this.lastPageY = iPageY;
17095         } else {
17096             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17097             this.lastPageX = aCoord[0];
17098             this.lastPageY = aCoord[1];
17099         }
17100     },
17101
17102     /**
17103      * Auto-scroll the window if the dragged object has been moved beyond the
17104      * visible window boundary.
17105      * @method autoScroll
17106      * @param {int} x the drag element's x position
17107      * @param {int} y the drag element's y position
17108      * @param {int} h the height of the drag element
17109      * @param {int} w the width of the drag element
17110      * @private
17111      */
17112     autoScroll: function(x, y, h, w) {
17113
17114         if (this.scroll) {
17115             // The client height
17116             var clientH = Roo.lib.Dom.getViewWidth();
17117
17118             // The client width
17119             var clientW = Roo.lib.Dom.getViewHeight();
17120
17121             // The amt scrolled down
17122             var st = this.DDM.getScrollTop();
17123
17124             // The amt scrolled right
17125             var sl = this.DDM.getScrollLeft();
17126
17127             // Location of the bottom of the element
17128             var bot = h + y;
17129
17130             // Location of the right of the element
17131             var right = w + x;
17132
17133             // The distance from the cursor to the bottom of the visible area,
17134             // adjusted so that we don't scroll if the cursor is beyond the
17135             // element drag constraints
17136             var toBot = (clientH + st - y - this.deltaY);
17137
17138             // The distance from the cursor to the right of the visible area
17139             var toRight = (clientW + sl - x - this.deltaX);
17140
17141
17142             // How close to the edge the cursor must be before we scroll
17143             // var thresh = (document.all) ? 100 : 40;
17144             var thresh = 40;
17145
17146             // How many pixels to scroll per autoscroll op.  This helps to reduce
17147             // clunky scrolling. IE is more sensitive about this ... it needs this
17148             // value to be higher.
17149             var scrAmt = (document.all) ? 80 : 30;
17150
17151             // Scroll down if we are near the bottom of the visible page and the
17152             // obj extends below the crease
17153             if ( bot > clientH && toBot < thresh ) {
17154                 window.scrollTo(sl, st + scrAmt);
17155             }
17156
17157             // Scroll up if the window is scrolled down and the top of the object
17158             // goes above the top border
17159             if ( y < st && st > 0 && y - st < thresh ) {
17160                 window.scrollTo(sl, st - scrAmt);
17161             }
17162
17163             // Scroll right if the obj is beyond the right border and the cursor is
17164             // near the border.
17165             if ( right > clientW && toRight < thresh ) {
17166                 window.scrollTo(sl + scrAmt, st);
17167             }
17168
17169             // Scroll left if the window has been scrolled to the right and the obj
17170             // extends past the left border
17171             if ( x < sl && sl > 0 && x - sl < thresh ) {
17172                 window.scrollTo(sl - scrAmt, st);
17173             }
17174         }
17175     },
17176
17177     /**
17178      * Finds the location the element should be placed if we want to move
17179      * it to where the mouse location less the click offset would place us.
17180      * @method getTargetCoord
17181      * @param {int} iPageX the X coordinate of the click
17182      * @param {int} iPageY the Y coordinate of the click
17183      * @return an object that contains the coordinates (Object.x and Object.y)
17184      * @private
17185      */
17186     getTargetCoord: function(iPageX, iPageY) {
17187
17188
17189         var x = iPageX - this.deltaX;
17190         var y = iPageY - this.deltaY;
17191
17192         if (this.constrainX) {
17193             if (x < this.minX) { x = this.minX; }
17194             if (x > this.maxX) { x = this.maxX; }
17195         }
17196
17197         if (this.constrainY) {
17198             if (y < this.minY) { y = this.minY; }
17199             if (y > this.maxY) { y = this.maxY; }
17200         }
17201
17202         x = this.getTick(x, this.xTicks);
17203         y = this.getTick(y, this.yTicks);
17204
17205
17206         return {x:x, y:y};
17207     },
17208
17209     /*
17210      * Sets up config options specific to this class. Overrides
17211      * Roo.dd.DragDrop, but all versions of this method through the
17212      * inheritance chain are called
17213      */
17214     applyConfig: function() {
17215         Roo.dd.DD.superclass.applyConfig.call(this);
17216         this.scroll = (this.config.scroll !== false);
17217     },
17218
17219     /*
17220      * Event that fires prior to the onMouseDown event.  Overrides
17221      * Roo.dd.DragDrop.
17222      */
17223     b4MouseDown: function(e) {
17224         // this.resetConstraints();
17225         this.autoOffset(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     /*
17230      * Event that fires prior to the onDrag event.  Overrides
17231      * Roo.dd.DragDrop.
17232      */
17233     b4Drag: function(e) {
17234         this.setDragElPos(e.getPageX(),
17235                             e.getPageY());
17236     },
17237
17238     toString: function() {
17239         return ("DD " + this.id);
17240     }
17241
17242     //////////////////////////////////////////////////////////////////////////
17243     // Debugging ygDragDrop events that can be overridden
17244     //////////////////////////////////////////////////////////////////////////
17245     /*
17246     startDrag: function(x, y) {
17247     },
17248
17249     onDrag: function(e) {
17250     },
17251
17252     onDragEnter: function(e, id) {
17253     },
17254
17255     onDragOver: function(e, id) {
17256     },
17257
17258     onDragOut: function(e, id) {
17259     },
17260
17261     onDragDrop: function(e, id) {
17262     },
17263
17264     endDrag: function(e) {
17265     }
17266
17267     */
17268
17269 });/*
17270  * Based on:
17271  * Ext JS Library 1.1.1
17272  * Copyright(c) 2006-2007, Ext JS, LLC.
17273  *
17274  * Originally Released Under LGPL - original licence link has changed is not relivant.
17275  *
17276  * Fork - LGPL
17277  * <script type="text/javascript">
17278  */
17279
17280 /**
17281  * @class Roo.dd.DDProxy
17282  * A DragDrop implementation that inserts an empty, bordered div into
17283  * the document that follows the cursor during drag operations.  At the time of
17284  * the click, the frame div is resized to the dimensions of the linked html
17285  * element, and moved to the exact location of the linked element.
17286  *
17287  * References to the "frame" element refer to the single proxy element that
17288  * was created to be dragged in place of all DDProxy elements on the
17289  * page.
17290  *
17291  * @extends Roo.dd.DD
17292  * @constructor
17293  * @param {String} id the id of the linked html element
17294  * @param {String} sGroup the group of related DragDrop objects
17295  * @param {object} config an object containing configurable attributes
17296  *                Valid properties for DDProxy in addition to those in DragDrop:
17297  *                   resizeFrame, centerFrame, dragElId
17298  */
17299 Roo.dd.DDProxy = function(id, sGroup, config) {
17300     if (id) {
17301         this.init(id, sGroup, config);
17302         this.initFrame();
17303     }
17304 };
17305
17306 /**
17307  * The default drag frame div id
17308  * @property Roo.dd.DDProxy.dragElId
17309  * @type String
17310  * @static
17311  */
17312 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17313
17314 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17315
17316     /**
17317      * By default we resize the drag frame to be the same size as the element
17318      * we want to drag (this is to get the frame effect).  We can turn it off
17319      * if we want a different behavior.
17320      * @property resizeFrame
17321      * @type boolean
17322      */
17323     resizeFrame: true,
17324
17325     /**
17326      * By default the frame is positioned exactly where the drag element is, so
17327      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17328      * you do not have constraints on the obj is to have the drag frame centered
17329      * around the cursor.  Set centerFrame to true for this effect.
17330      * @property centerFrame
17331      * @type boolean
17332      */
17333     centerFrame: false,
17334
17335     /**
17336      * Creates the proxy element if it does not yet exist
17337      * @method createFrame
17338      */
17339     createFrame: function() {
17340         var self = this;
17341         var body = document.body;
17342
17343         if (!body || !body.firstChild) {
17344             setTimeout( function() { self.createFrame(); }, 50 );
17345             return;
17346         }
17347
17348         var div = this.getDragEl();
17349
17350         if (!div) {
17351             div    = document.createElement("div");
17352             div.id = this.dragElId;
17353             var s  = div.style;
17354
17355             s.position   = "absolute";
17356             s.visibility = "hidden";
17357             s.cursor     = "move";
17358             s.border     = "2px solid #aaa";
17359             s.zIndex     = 999;
17360
17361             // appendChild can blow up IE if invoked prior to the window load event
17362             // while rendering a table.  It is possible there are other scenarios
17363             // that would cause this to happen as well.
17364             body.insertBefore(div, body.firstChild);
17365         }
17366     },
17367
17368     /**
17369      * Initialization for the drag frame element.  Must be called in the
17370      * constructor of all subclasses
17371      * @method initFrame
17372      */
17373     initFrame: function() {
17374         this.createFrame();
17375     },
17376
17377     applyConfig: function() {
17378         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17379
17380         this.resizeFrame = (this.config.resizeFrame !== false);
17381         this.centerFrame = (this.config.centerFrame);
17382         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17383     },
17384
17385     /**
17386      * Resizes the drag frame to the dimensions of the clicked object, positions
17387      * it over the object, and finally displays it
17388      * @method showFrame
17389      * @param {int} iPageX X click position
17390      * @param {int} iPageY Y click position
17391      * @private
17392      */
17393     showFrame: function(iPageX, iPageY) {
17394         var el = this.getEl();
17395         var dragEl = this.getDragEl();
17396         var s = dragEl.style;
17397
17398         this._resizeProxy();
17399
17400         if (this.centerFrame) {
17401             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17402                            Math.round(parseInt(s.height, 10)/2) );
17403         }
17404
17405         this.setDragElPos(iPageX, iPageY);
17406
17407         Roo.fly(dragEl).show();
17408     },
17409
17410     /**
17411      * The proxy is automatically resized to the dimensions of the linked
17412      * element when a drag is initiated, unless resizeFrame is set to false
17413      * @method _resizeProxy
17414      * @private
17415      */
17416     _resizeProxy: function() {
17417         if (this.resizeFrame) {
17418             var el = this.getEl();
17419             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17420         }
17421     },
17422
17423     // overrides Roo.dd.DragDrop
17424     b4MouseDown: function(e) {
17425         var x = e.getPageX();
17426         var y = e.getPageY();
17427         this.autoOffset(x, y);
17428         this.setDragElPos(x, y);
17429     },
17430
17431     // overrides Roo.dd.DragDrop
17432     b4StartDrag: function(x, y) {
17433         // show the drag frame
17434         this.showFrame(x, y);
17435     },
17436
17437     // overrides Roo.dd.DragDrop
17438     b4EndDrag: function(e) {
17439         Roo.fly(this.getDragEl()).hide();
17440     },
17441
17442     // overrides Roo.dd.DragDrop
17443     // By default we try to move the element to the last location of the frame.
17444     // This is so that the default behavior mirrors that of Roo.dd.DD.
17445     endDrag: function(e) {
17446
17447         var lel = this.getEl();
17448         var del = this.getDragEl();
17449
17450         // Show the drag frame briefly so we can get its position
17451         del.style.visibility = "";
17452
17453         this.beforeMove();
17454         // Hide the linked element before the move to get around a Safari
17455         // rendering bug.
17456         lel.style.visibility = "hidden";
17457         Roo.dd.DDM.moveToEl(lel, del);
17458         del.style.visibility = "hidden";
17459         lel.style.visibility = "";
17460
17461         this.afterDrag();
17462     },
17463
17464     beforeMove : function(){
17465
17466     },
17467
17468     afterDrag : function(){
17469
17470     },
17471
17472     toString: function() {
17473         return ("DDProxy " + this.id);
17474     }
17475
17476 });
17477 /*
17478  * Based on:
17479  * Ext JS Library 1.1.1
17480  * Copyright(c) 2006-2007, Ext JS, LLC.
17481  *
17482  * Originally Released Under LGPL - original licence link has changed is not relivant.
17483  *
17484  * Fork - LGPL
17485  * <script type="text/javascript">
17486  */
17487
17488  /**
17489  * @class Roo.dd.DDTarget
17490  * A DragDrop implementation that does not move, but can be a drop
17491  * target.  You would get the same result by simply omitting implementation
17492  * for the event callbacks, but this way we reduce the processing cost of the
17493  * event listener and the callbacks.
17494  * @extends Roo.dd.DragDrop
17495  * @constructor
17496  * @param {String} id the id of the element that is a drop target
17497  * @param {String} sGroup the group of related DragDrop objects
17498  * @param {object} config an object containing configurable attributes
17499  *                 Valid properties for DDTarget in addition to those in
17500  *                 DragDrop:
17501  *                    none
17502  */
17503 Roo.dd.DDTarget = function(id, sGroup, config) {
17504     if (id) {
17505         this.initTarget(id, sGroup, config);
17506     }
17507     if (config.listeners || config.events) { 
17508        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17509             listeners : config.listeners || {}, 
17510             events : config.events || {} 
17511         });    
17512     }
17513 };
17514
17515 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17516 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17517     toString: function() {
17518         return ("DDTarget " + this.id);
17519     }
17520 });
17521 /*
17522  * Based on:
17523  * Ext JS Library 1.1.1
17524  * Copyright(c) 2006-2007, Ext JS, LLC.
17525  *
17526  * Originally Released Under LGPL - original licence link has changed is not relivant.
17527  *
17528  * Fork - LGPL
17529  * <script type="text/javascript">
17530  */
17531  
17532
17533 /**
17534  * @class Roo.dd.ScrollManager
17535  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17536  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17537  * @singleton
17538  */
17539 Roo.dd.ScrollManager = function(){
17540     var ddm = Roo.dd.DragDropMgr;
17541     var els = {};
17542     var dragEl = null;
17543     var proc = {};
17544     
17545     var onStop = function(e){
17546         dragEl = null;
17547         clearProc();
17548     };
17549     
17550     var triggerRefresh = function(){
17551         if(ddm.dragCurrent){
17552              ddm.refreshCache(ddm.dragCurrent.groups);
17553         }
17554     };
17555     
17556     var doScroll = function(){
17557         if(ddm.dragCurrent){
17558             var dds = Roo.dd.ScrollManager;
17559             if(!dds.animate){
17560                 if(proc.el.scroll(proc.dir, dds.increment)){
17561                     triggerRefresh();
17562                 }
17563             }else{
17564                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17565             }
17566         }
17567     };
17568     
17569     var clearProc = function(){
17570         if(proc.id){
17571             clearInterval(proc.id);
17572         }
17573         proc.id = 0;
17574         proc.el = null;
17575         proc.dir = "";
17576     };
17577     
17578     var startProc = function(el, dir){
17579         clearProc();
17580         proc.el = el;
17581         proc.dir = dir;
17582         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17583     };
17584     
17585     var onFire = function(e, isDrop){
17586         if(isDrop || !ddm.dragCurrent){ return; }
17587         var dds = Roo.dd.ScrollManager;
17588         if(!dragEl || dragEl != ddm.dragCurrent){
17589             dragEl = ddm.dragCurrent;
17590             // refresh regions on drag start
17591             dds.refreshCache();
17592         }
17593         
17594         var xy = Roo.lib.Event.getXY(e);
17595         var pt = new Roo.lib.Point(xy[0], xy[1]);
17596         for(var id in els){
17597             var el = els[id], r = el._region;
17598             if(r && r.contains(pt) && el.isScrollable()){
17599                 if(r.bottom - pt.y <= dds.thresh){
17600                     if(proc.el != el){
17601                         startProc(el, "down");
17602                     }
17603                     return;
17604                 }else if(r.right - pt.x <= dds.thresh){
17605                     if(proc.el != el){
17606                         startProc(el, "left");
17607                     }
17608                     return;
17609                 }else if(pt.y - r.top <= dds.thresh){
17610                     if(proc.el != el){
17611                         startProc(el, "up");
17612                     }
17613                     return;
17614                 }else if(pt.x - r.left <= dds.thresh){
17615                     if(proc.el != el){
17616                         startProc(el, "right");
17617                     }
17618                     return;
17619                 }
17620             }
17621         }
17622         clearProc();
17623     };
17624     
17625     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17626     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17627     
17628     return {
17629         /**
17630          * Registers new overflow element(s) to auto scroll
17631          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17632          */
17633         register : function(el){
17634             if(el instanceof Array){
17635                 for(var i = 0, len = el.length; i < len; i++) {
17636                         this.register(el[i]);
17637                 }
17638             }else{
17639                 el = Roo.get(el);
17640                 els[el.id] = el;
17641             }
17642         },
17643         
17644         /**
17645          * Unregisters overflow element(s) so they are no longer scrolled
17646          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17647          */
17648         unregister : function(el){
17649             if(el instanceof Array){
17650                 for(var i = 0, len = el.length; i < len; i++) {
17651                         this.unregister(el[i]);
17652                 }
17653             }else{
17654                 el = Roo.get(el);
17655                 delete els[el.id];
17656             }
17657         },
17658         
17659         /**
17660          * The number of pixels from the edge of a container the pointer needs to be to 
17661          * trigger scrolling (defaults to 25)
17662          * @type Number
17663          */
17664         thresh : 25,
17665         
17666         /**
17667          * The number of pixels to scroll in each scroll increment (defaults to 50)
17668          * @type Number
17669          */
17670         increment : 100,
17671         
17672         /**
17673          * The frequency of scrolls in milliseconds (defaults to 500)
17674          * @type Number
17675          */
17676         frequency : 500,
17677         
17678         /**
17679          * True to animate the scroll (defaults to true)
17680          * @type Boolean
17681          */
17682         animate: true,
17683         
17684         /**
17685          * The animation duration in seconds - 
17686          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17687          * @type Number
17688          */
17689         animDuration: .4,
17690         
17691         /**
17692          * Manually trigger a cache refresh.
17693          */
17694         refreshCache : function(){
17695             for(var id in els){
17696                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17697                     els[id]._region = els[id].getRegion();
17698                 }
17699             }
17700         }
17701     };
17702 }();/*
17703  * Based on:
17704  * Ext JS Library 1.1.1
17705  * Copyright(c) 2006-2007, Ext JS, LLC.
17706  *
17707  * Originally Released Under LGPL - original licence link has changed is not relivant.
17708  *
17709  * Fork - LGPL
17710  * <script type="text/javascript">
17711  */
17712  
17713
17714 /**
17715  * @class Roo.dd.Registry
17716  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17717  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17718  * @singleton
17719  */
17720 Roo.dd.Registry = function(){
17721     var elements = {}; 
17722     var handles = {}; 
17723     var autoIdSeed = 0;
17724
17725     var getId = function(el, autogen){
17726         if(typeof el == "string"){
17727             return el;
17728         }
17729         var id = el.id;
17730         if(!id && autogen !== false){
17731             id = "roodd-" + (++autoIdSeed);
17732             el.id = id;
17733         }
17734         return id;
17735     };
17736     
17737     return {
17738     /**
17739      * Register a drag drop element
17740      * @param {String|HTMLElement} element The id or DOM node to register
17741      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17742      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17743      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17744      * populated in the data object (if applicable):
17745      * <pre>
17746 Value      Description<br />
17747 ---------  ------------------------------------------<br />
17748 handles    Array of DOM nodes that trigger dragging<br />
17749            for the element being registered<br />
17750 isHandle   True if the element passed in triggers<br />
17751            dragging itself, else false
17752 </pre>
17753      */
17754         register : function(el, data){
17755             data = data || {};
17756             if(typeof el == "string"){
17757                 el = document.getElementById(el);
17758             }
17759             data.ddel = el;
17760             elements[getId(el)] = data;
17761             if(data.isHandle !== false){
17762                 handles[data.ddel.id] = data;
17763             }
17764             if(data.handles){
17765                 var hs = data.handles;
17766                 for(var i = 0, len = hs.length; i < len; i++){
17767                         handles[getId(hs[i])] = data;
17768                 }
17769             }
17770         },
17771
17772     /**
17773      * Unregister a drag drop element
17774      * @param {String|HTMLElement}  element The id or DOM node to unregister
17775      */
17776         unregister : function(el){
17777             var id = getId(el, false);
17778             var data = elements[id];
17779             if(data){
17780                 delete elements[id];
17781                 if(data.handles){
17782                     var hs = data.handles;
17783                     for(var i = 0, len = hs.length; i < len; i++){
17784                         delete handles[getId(hs[i], false)];
17785                     }
17786                 }
17787             }
17788         },
17789
17790     /**
17791      * Returns the handle registered for a DOM Node by id
17792      * @param {String|HTMLElement} id The DOM node or id to look up
17793      * @return {Object} handle The custom handle data
17794      */
17795         getHandle : function(id){
17796             if(typeof id != "string"){ // must be element?
17797                 id = id.id;
17798             }
17799             return handles[id];
17800         },
17801
17802     /**
17803      * Returns the handle that is registered for the DOM node that is the target of the event
17804      * @param {Event} e The event
17805      * @return {Object} handle The custom handle data
17806      */
17807         getHandleFromEvent : function(e){
17808             var t = Roo.lib.Event.getTarget(e);
17809             return t ? handles[t.id] : null;
17810         },
17811
17812     /**
17813      * Returns a custom data object that is registered for a DOM node by id
17814      * @param {String|HTMLElement} id The DOM node or id to look up
17815      * @return {Object} data The custom data
17816      */
17817         getTarget : function(id){
17818             if(typeof id != "string"){ // must be element?
17819                 id = id.id;
17820             }
17821             return elements[id];
17822         },
17823
17824     /**
17825      * Returns a custom data object that is registered for the DOM node that is the target of the event
17826      * @param {Event} e The event
17827      * @return {Object} data The custom data
17828      */
17829         getTargetFromEvent : function(e){
17830             var t = Roo.lib.Event.getTarget(e);
17831             return t ? elements[t.id] || handles[t.id] : null;
17832         }
17833     };
17834 }();/*
17835  * Based on:
17836  * Ext JS Library 1.1.1
17837  * Copyright(c) 2006-2007, Ext JS, LLC.
17838  *
17839  * Originally Released Under LGPL - original licence link has changed is not relivant.
17840  *
17841  * Fork - LGPL
17842  * <script type="text/javascript">
17843  */
17844  
17845
17846 /**
17847  * @class Roo.dd.StatusProxy
17848  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17849  * default drag proxy used by all Roo.dd components.
17850  * @constructor
17851  * @param {Object} config
17852  */
17853 Roo.dd.StatusProxy = function(config){
17854     Roo.apply(this, config);
17855     this.id = this.id || Roo.id();
17856     this.el = new Roo.Layer({
17857         dh: {
17858             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17859                 {tag: "div", cls: "x-dd-drop-icon"},
17860                 {tag: "div", cls: "x-dd-drag-ghost"}
17861             ]
17862         }, 
17863         shadow: !config || config.shadow !== false
17864     });
17865     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17866     this.dropStatus = this.dropNotAllowed;
17867 };
17868
17869 Roo.dd.StatusProxy.prototype = {
17870     /**
17871      * @cfg {String} dropAllowed
17872      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17873      */
17874     dropAllowed : "x-dd-drop-ok",
17875     /**
17876      * @cfg {String} dropNotAllowed
17877      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17878      */
17879     dropNotAllowed : "x-dd-drop-nodrop",
17880
17881     /**
17882      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17883      * over the current target element.
17884      * @param {String} cssClass The css class for the new drop status indicator image
17885      */
17886     setStatus : function(cssClass){
17887         cssClass = cssClass || this.dropNotAllowed;
17888         if(this.dropStatus != cssClass){
17889             this.el.replaceClass(this.dropStatus, cssClass);
17890             this.dropStatus = cssClass;
17891         }
17892     },
17893
17894     /**
17895      * Resets the status indicator to the default dropNotAllowed value
17896      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17897      */
17898     reset : function(clearGhost){
17899         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17900         this.dropStatus = this.dropNotAllowed;
17901         if(clearGhost){
17902             this.ghost.update("");
17903         }
17904     },
17905
17906     /**
17907      * Updates the contents of the ghost element
17908      * @param {String} html The html that will replace the current innerHTML of the ghost element
17909      */
17910     update : function(html){
17911         if(typeof html == "string"){
17912             this.ghost.update(html);
17913         }else{
17914             this.ghost.update("");
17915             html.style.margin = "0";
17916             this.ghost.dom.appendChild(html);
17917         }
17918         // ensure float = none set?? cant remember why though.
17919         var el = this.ghost.dom.firstChild;
17920                 if(el){
17921                         Roo.fly(el).setStyle('float', 'none');
17922                 }
17923     },
17924     
17925     /**
17926      * Returns the underlying proxy {@link Roo.Layer}
17927      * @return {Roo.Layer} el
17928     */
17929     getEl : function(){
17930         return this.el;
17931     },
17932
17933     /**
17934      * Returns the ghost element
17935      * @return {Roo.Element} el
17936      */
17937     getGhost : function(){
17938         return this.ghost;
17939     },
17940
17941     /**
17942      * Hides the proxy
17943      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17944      */
17945     hide : function(clear){
17946         this.el.hide();
17947         if(clear){
17948             this.reset(true);
17949         }
17950     },
17951
17952     /**
17953      * Stops the repair animation if it's currently running
17954      */
17955     stop : function(){
17956         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17957             this.anim.stop();
17958         }
17959     },
17960
17961     /**
17962      * Displays this proxy
17963      */
17964     show : function(){
17965         this.el.show();
17966     },
17967
17968     /**
17969      * Force the Layer to sync its shadow and shim positions to the element
17970      */
17971     sync : function(){
17972         this.el.sync();
17973     },
17974
17975     /**
17976      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17977      * invalid drop operation by the item being dragged.
17978      * @param {Array} xy The XY position of the element ([x, y])
17979      * @param {Function} callback The function to call after the repair is complete
17980      * @param {Object} scope The scope in which to execute the callback
17981      */
17982     repair : function(xy, callback, scope){
17983         this.callback = callback;
17984         this.scope = scope;
17985         if(xy && this.animRepair !== false){
17986             this.el.addClass("x-dd-drag-repair");
17987             this.el.hideUnders(true);
17988             this.anim = this.el.shift({
17989                 duration: this.repairDuration || .5,
17990                 easing: 'easeOut',
17991                 xy: xy,
17992                 stopFx: true,
17993                 callback: this.afterRepair,
17994                 scope: this
17995             });
17996         }else{
17997             this.afterRepair();
17998         }
17999     },
18000
18001     // private
18002     afterRepair : function(){
18003         this.hide(true);
18004         if(typeof this.callback == "function"){
18005             this.callback.call(this.scope || this);
18006         }
18007         this.callback = null;
18008         this.scope = null;
18009     }
18010 };/*
18011  * Based on:
18012  * Ext JS Library 1.1.1
18013  * Copyright(c) 2006-2007, Ext JS, LLC.
18014  *
18015  * Originally Released Under LGPL - original licence link has changed is not relivant.
18016  *
18017  * Fork - LGPL
18018  * <script type="text/javascript">
18019  */
18020
18021 /**
18022  * @class Roo.dd.DragSource
18023  * @extends Roo.dd.DDProxy
18024  * A simple class that provides the basic implementation needed to make any element draggable.
18025  * @constructor
18026  * @param {String/HTMLElement/Element} el The container element
18027  * @param {Object} config
18028  */
18029 Roo.dd.DragSource = function(el, config){
18030     this.el = Roo.get(el);
18031     this.dragData = {};
18032     
18033     Roo.apply(this, config);
18034     
18035     if(!this.proxy){
18036         this.proxy = new Roo.dd.StatusProxy();
18037     }
18038
18039     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18040           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18041     
18042     this.dragging = false;
18043 };
18044
18045 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18046     /**
18047      * @cfg {String} dropAllowed
18048      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18049      */
18050     dropAllowed : "x-dd-drop-ok",
18051     /**
18052      * @cfg {String} dropNotAllowed
18053      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18054      */
18055     dropNotAllowed : "x-dd-drop-nodrop",
18056
18057     /**
18058      * Returns the data object associated with this drag source
18059      * @return {Object} data An object containing arbitrary data
18060      */
18061     getDragData : function(e){
18062         return this.dragData;
18063     },
18064
18065     // private
18066     onDragEnter : function(e, id){
18067         var target = Roo.dd.DragDropMgr.getDDById(id);
18068         this.cachedTarget = target;
18069         if(this.beforeDragEnter(target, e, id) !== false){
18070             if(target.isNotifyTarget){
18071                 var status = target.notifyEnter(this, e, this.dragData);
18072                 this.proxy.setStatus(status);
18073             }else{
18074                 this.proxy.setStatus(this.dropAllowed);
18075             }
18076             
18077             if(this.afterDragEnter){
18078                 /**
18079                  * An empty function by default, but provided so that you can perform a custom action
18080                  * when the dragged item enters the drop target by providing an implementation.
18081                  * @param {Roo.dd.DragDrop} target The drop target
18082                  * @param {Event} e The event object
18083                  * @param {String} id The id of the dragged element
18084                  * @method afterDragEnter
18085                  */
18086                 this.afterDragEnter(target, e, id);
18087             }
18088         }
18089     },
18090
18091     /**
18092      * An empty function by default, but provided so that you can perform a custom action
18093      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18094      * @param {Roo.dd.DragDrop} target The drop target
18095      * @param {Event} e The event object
18096      * @param {String} id The id of the dragged element
18097      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18098      */
18099     beforeDragEnter : function(target, e, id){
18100         return true;
18101     },
18102
18103     // private
18104     alignElWithMouse: function() {
18105         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18106         this.proxy.sync();
18107     },
18108
18109     // private
18110     onDragOver : function(e, id){
18111         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18112         if(this.beforeDragOver(target, e, id) !== false){
18113             if(target.isNotifyTarget){
18114                 var status = target.notifyOver(this, e, this.dragData);
18115                 this.proxy.setStatus(status);
18116             }
18117
18118             if(this.afterDragOver){
18119                 /**
18120                  * An empty function by default, but provided so that you can perform a custom action
18121                  * while the dragged item is over the drop target by providing an implementation.
18122                  * @param {Roo.dd.DragDrop} target The drop target
18123                  * @param {Event} e The event object
18124                  * @param {String} id The id of the dragged element
18125                  * @method afterDragOver
18126                  */
18127                 this.afterDragOver(target, e, id);
18128             }
18129         }
18130     },
18131
18132     /**
18133      * An empty function by default, but provided so that you can perform a custom action
18134      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18135      * @param {Roo.dd.DragDrop} target The drop target
18136      * @param {Event} e The event object
18137      * @param {String} id The id of the dragged element
18138      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18139      */
18140     beforeDragOver : function(target, e, id){
18141         return true;
18142     },
18143
18144     // private
18145     onDragOut : function(e, id){
18146         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18147         if(this.beforeDragOut(target, e, id) !== false){
18148             if(target.isNotifyTarget){
18149                 target.notifyOut(this, e, this.dragData);
18150             }
18151             this.proxy.reset();
18152             if(this.afterDragOut){
18153                 /**
18154                  * An empty function by default, but provided so that you can perform a custom action
18155                  * after the dragged item is dragged out of the target without dropping.
18156                  * @param {Roo.dd.DragDrop} target The drop target
18157                  * @param {Event} e The event object
18158                  * @param {String} id The id of the dragged element
18159                  * @method afterDragOut
18160                  */
18161                 this.afterDragOut(target, e, id);
18162             }
18163         }
18164         this.cachedTarget = null;
18165     },
18166
18167     /**
18168      * An empty function by default, but provided so that you can perform a custom action before the dragged
18169      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18170      * @param {Roo.dd.DragDrop} target The drop target
18171      * @param {Event} e The event object
18172      * @param {String} id The id of the dragged element
18173      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18174      */
18175     beforeDragOut : function(target, e, id){
18176         return true;
18177     },
18178     
18179     // private
18180     onDragDrop : function(e, id){
18181         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18182         if(this.beforeDragDrop(target, e, id) !== false){
18183             if(target.isNotifyTarget){
18184                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18185                     this.onValidDrop(target, e, id);
18186                 }else{
18187                     this.onInvalidDrop(target, e, id);
18188                 }
18189             }else{
18190                 this.onValidDrop(target, e, id);
18191             }
18192             
18193             if(this.afterDragDrop){
18194                 /**
18195                  * An empty function by default, but provided so that you can perform a custom action
18196                  * after a valid drag drop has occurred by providing an implementation.
18197                  * @param {Roo.dd.DragDrop} target The drop target
18198                  * @param {Event} e The event object
18199                  * @param {String} id The id of the dropped element
18200                  * @method afterDragDrop
18201                  */
18202                 this.afterDragDrop(target, e, id);
18203             }
18204         }
18205         delete this.cachedTarget;
18206     },
18207
18208     /**
18209      * An empty function by default, but provided so that you can perform a custom action before the dragged
18210      * item is dropped onto the target and optionally cancel the onDragDrop.
18211      * @param {Roo.dd.DragDrop} target The drop target
18212      * @param {Event} e The event object
18213      * @param {String} id The id of the dragged element
18214      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18215      */
18216     beforeDragDrop : function(target, e, id){
18217         return true;
18218     },
18219
18220     // private
18221     onValidDrop : function(target, e, id){
18222         this.hideProxy();
18223         if(this.afterValidDrop){
18224             /**
18225              * An empty function by default, but provided so that you can perform a custom action
18226              * after a valid drop has occurred by providing an implementation.
18227              * @param {Object} target The target DD 
18228              * @param {Event} e The event object
18229              * @param {String} id The id of the dropped element
18230              * @method afterInvalidDrop
18231              */
18232             this.afterValidDrop(target, e, id);
18233         }
18234     },
18235
18236     // private
18237     getRepairXY : function(e, data){
18238         return this.el.getXY();  
18239     },
18240
18241     // private
18242     onInvalidDrop : function(target, e, id){
18243         this.beforeInvalidDrop(target, e, id);
18244         if(this.cachedTarget){
18245             if(this.cachedTarget.isNotifyTarget){
18246                 this.cachedTarget.notifyOut(this, e, this.dragData);
18247             }
18248             this.cacheTarget = null;
18249         }
18250         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18251
18252         if(this.afterInvalidDrop){
18253             /**
18254              * An empty function by default, but provided so that you can perform a custom action
18255              * after an invalid drop has occurred by providing an implementation.
18256              * @param {Event} e The event object
18257              * @param {String} id The id of the dropped element
18258              * @method afterInvalidDrop
18259              */
18260             this.afterInvalidDrop(e, id);
18261         }
18262     },
18263
18264     // private
18265     afterRepair : function(){
18266         if(Roo.enableFx){
18267             this.el.highlight(this.hlColor || "c3daf9");
18268         }
18269         this.dragging = false;
18270     },
18271
18272     /**
18273      * An empty function by default, but provided so that you can perform a custom action after an invalid
18274      * drop has occurred.
18275      * @param {Roo.dd.DragDrop} target The drop target
18276      * @param {Event} e The event object
18277      * @param {String} id The id of the dragged element
18278      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18279      */
18280     beforeInvalidDrop : function(target, e, id){
18281         return true;
18282     },
18283
18284     // private
18285     handleMouseDown : function(e){
18286         if(this.dragging) {
18287             return;
18288         }
18289         var data = this.getDragData(e);
18290         if(data && this.onBeforeDrag(data, e) !== false){
18291             this.dragData = data;
18292             this.proxy.stop();
18293             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18294         } 
18295     },
18296
18297     /**
18298      * An empty function by default, but provided so that you can perform a custom action before the initial
18299      * drag event begins and optionally cancel it.
18300      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18301      * @param {Event} e The event object
18302      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18303      */
18304     onBeforeDrag : function(data, e){
18305         return true;
18306     },
18307
18308     /**
18309      * An empty function by default, but provided so that you can perform a custom action once the initial
18310      * drag event has begun.  The drag cannot be canceled from this function.
18311      * @param {Number} x The x position of the click on the dragged object
18312      * @param {Number} y The y position of the click on the dragged object
18313      */
18314     onStartDrag : Roo.emptyFn,
18315
18316     // private - YUI override
18317     startDrag : function(x, y){
18318         this.proxy.reset();
18319         this.dragging = true;
18320         this.proxy.update("");
18321         this.onInitDrag(x, y);
18322         this.proxy.show();
18323     },
18324
18325     // private
18326     onInitDrag : function(x, y){
18327         var clone = this.el.dom.cloneNode(true);
18328         clone.id = Roo.id(); // prevent duplicate ids
18329         this.proxy.update(clone);
18330         this.onStartDrag(x, y);
18331         return true;
18332     },
18333
18334     /**
18335      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18336      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18337      */
18338     getProxy : function(){
18339         return this.proxy;  
18340     },
18341
18342     /**
18343      * Hides the drag source's {@link Roo.dd.StatusProxy}
18344      */
18345     hideProxy : function(){
18346         this.proxy.hide();  
18347         this.proxy.reset(true);
18348         this.dragging = false;
18349     },
18350
18351     // private
18352     triggerCacheRefresh : function(){
18353         Roo.dd.DDM.refreshCache(this.groups);
18354     },
18355
18356     // private - override to prevent hiding
18357     b4EndDrag: function(e) {
18358     },
18359
18360     // private - override to prevent moving
18361     endDrag : function(e){
18362         this.onEndDrag(this.dragData, e);
18363     },
18364
18365     // private
18366     onEndDrag : function(data, e){
18367     },
18368     
18369     // private - pin to cursor
18370     autoOffset : function(x, y) {
18371         this.setDelta(-12, -20);
18372     }    
18373 });/*
18374  * Based on:
18375  * Ext JS Library 1.1.1
18376  * Copyright(c) 2006-2007, Ext JS, LLC.
18377  *
18378  * Originally Released Under LGPL - original licence link has changed is not relivant.
18379  *
18380  * Fork - LGPL
18381  * <script type="text/javascript">
18382  */
18383
18384
18385 /**
18386  * @class Roo.dd.DropTarget
18387  * @extends Roo.dd.DDTarget
18388  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18389  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18390  * @constructor
18391  * @param {String/HTMLElement/Element} el The container element
18392  * @param {Object} config
18393  */
18394 Roo.dd.DropTarget = function(el, config){
18395     this.el = Roo.get(el);
18396     
18397     var listeners = false; ;
18398     if (config && config.listeners) {
18399         listeners= config.listeners;
18400         delete config.listeners;
18401     }
18402     Roo.apply(this, config);
18403     
18404     if(this.containerScroll){
18405         Roo.dd.ScrollManager.register(this.el);
18406     }
18407     this.addEvents( {
18408          /**
18409          * @scope Roo.dd.DropTarget
18410          */
18411          
18412          /**
18413          * @event enter
18414          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18415          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18416          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18417          * 
18418          * IMPORTANT : it should set this.overClass and this.dropAllowed
18419          * 
18420          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18421          * @param {Event} e The event
18422          * @param {Object} data An object containing arbitrary data supplied by the drag source
18423          */
18424         "enter" : true,
18425         
18426          /**
18427          * @event over
18428          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18429          * This method will be called on every mouse movement while the drag source is over the drop target.
18430          * This default implementation simply returns the dropAllowed config value.
18431          * 
18432          * IMPORTANT : it should set this.dropAllowed
18433          * 
18434          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18435          * @param {Event} e The event
18436          * @param {Object} data An object containing arbitrary data supplied by the drag source
18437          
18438          */
18439         "over" : true,
18440         /**
18441          * @event out
18442          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18443          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18444          * overClass (if any) from the drop element.
18445          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18446          * @param {Event} e The event
18447          * @param {Object} data An object containing arbitrary data supplied by the drag source
18448          */
18449          "out" : true,
18450          
18451         /**
18452          * @event drop
18453          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18454          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18455          * implementation that does something to process the drop event and returns true so that the drag source's
18456          * repair action does not run.
18457          * 
18458          * IMPORTANT : it should set this.success
18459          * 
18460          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18461          * @param {Event} e The event
18462          * @param {Object} data An object containing arbitrary data supplied by the drag source
18463         */
18464          "drop" : true
18465     });
18466             
18467      
18468     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18469         this.el.dom, 
18470         this.ddGroup || this.group,
18471         {
18472             isTarget: true,
18473             listeners : listeners || {} 
18474            
18475         
18476         }
18477     );
18478
18479 };
18480
18481 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18482     /**
18483      * @cfg {String} overClass
18484      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18485      */
18486      /**
18487      * @cfg {String} ddGroup
18488      * The drag drop group to handle drop events for
18489      */
18490      
18491     /**
18492      * @cfg {String} dropAllowed
18493      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18494      */
18495     dropAllowed : "x-dd-drop-ok",
18496     /**
18497      * @cfg {String} dropNotAllowed
18498      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18499      */
18500     dropNotAllowed : "x-dd-drop-nodrop",
18501     /**
18502      * @cfg {boolean} success
18503      * set this after drop listener.. 
18504      */
18505     success : false,
18506     /**
18507      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18508      * if the drop point is valid for over/enter..
18509      */
18510     valid : false,
18511     // private
18512     isTarget : true,
18513
18514     // private
18515     isNotifyTarget : true,
18516     
18517     /**
18518      * @hide
18519      */
18520     notifyEnter : function(dd, e, data)
18521     {
18522         this.valid = true;
18523         this.fireEvent('enter', dd, e, data);
18524         if(this.overClass){
18525             this.el.addClass(this.overClass);
18526         }
18527         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18528             this.valid ? this.dropAllowed : this.dropNotAllowed
18529         );
18530     },
18531
18532     /**
18533      * @hide
18534      */
18535     notifyOver : function(dd, e, data)
18536     {
18537         this.valid = true;
18538         this.fireEvent('over', dd, e, data);
18539         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18540             this.valid ? this.dropAllowed : this.dropNotAllowed
18541         );
18542     },
18543
18544     /**
18545      * @hide
18546      */
18547     notifyOut : function(dd, e, data)
18548     {
18549         this.fireEvent('out', dd, e, data);
18550         if(this.overClass){
18551             this.el.removeClass(this.overClass);
18552         }
18553     },
18554
18555     /**
18556      * @hide
18557      */
18558     notifyDrop : function(dd, e, data)
18559     {
18560         this.success = false;
18561         this.fireEvent('drop', dd, e, data);
18562         return this.success;
18563     }
18564 });/*
18565  * Based on:
18566  * Ext JS Library 1.1.1
18567  * Copyright(c) 2006-2007, Ext JS, LLC.
18568  *
18569  * Originally Released Under LGPL - original licence link has changed is not relivant.
18570  *
18571  * Fork - LGPL
18572  * <script type="text/javascript">
18573  */
18574
18575
18576 /**
18577  * @class Roo.dd.DragZone
18578  * @extends Roo.dd.DragSource
18579  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18580  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18581  * @constructor
18582  * @param {String/HTMLElement/Element} el The container element
18583  * @param {Object} config
18584  */
18585 Roo.dd.DragZone = function(el, config){
18586     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18587     if(this.containerScroll){
18588         Roo.dd.ScrollManager.register(this.el);
18589     }
18590 };
18591
18592 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18593     /**
18594      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18595      * for auto scrolling during drag operations.
18596      */
18597     /**
18598      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18599      * method after a failed drop (defaults to "c3daf9" - light blue)
18600      */
18601
18602     /**
18603      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18604      * for a valid target to drag based on the mouse down. Override this method
18605      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18606      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18607      * @param {EventObject} e The mouse down event
18608      * @return {Object} The dragData
18609      */
18610     getDragData : function(e){
18611         return Roo.dd.Registry.getHandleFromEvent(e);
18612     },
18613     
18614     /**
18615      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18616      * this.dragData.ddel
18617      * @param {Number} x The x position of the click on the dragged object
18618      * @param {Number} y The y position of the click on the dragged object
18619      * @return {Boolean} true to continue the drag, false to cancel
18620      */
18621     onInitDrag : function(x, y){
18622         this.proxy.update(this.dragData.ddel.cloneNode(true));
18623         this.onStartDrag(x, y);
18624         return true;
18625     },
18626     
18627     /**
18628      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18629      */
18630     afterRepair : function(){
18631         if(Roo.enableFx){
18632             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18633         }
18634         this.dragging = false;
18635     },
18636
18637     /**
18638      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18639      * the XY of this.dragData.ddel
18640      * @param {EventObject} e The mouse up event
18641      * @return {Array} The xy location (e.g. [100, 200])
18642      */
18643     getRepairXY : function(e){
18644         return Roo.Element.fly(this.dragData.ddel).getXY();  
18645     }
18646 });/*
18647  * Based on:
18648  * Ext JS Library 1.1.1
18649  * Copyright(c) 2006-2007, Ext JS, LLC.
18650  *
18651  * Originally Released Under LGPL - original licence link has changed is not relivant.
18652  *
18653  * Fork - LGPL
18654  * <script type="text/javascript">
18655  */
18656 /**
18657  * @class Roo.dd.DropZone
18658  * @extends Roo.dd.DropTarget
18659  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18660  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18661  * @constructor
18662  * @param {String/HTMLElement/Element} el The container element
18663  * @param {Object} config
18664  */
18665 Roo.dd.DropZone = function(el, config){
18666     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18667 };
18668
18669 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18670     /**
18671      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18672      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18673      * provide your own custom lookup.
18674      * @param {Event} e The event
18675      * @return {Object} data The custom data
18676      */
18677     getTargetFromEvent : function(e){
18678         return Roo.dd.Registry.getTargetFromEvent(e);
18679     },
18680
18681     /**
18682      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18683      * that it has registered.  This method has no default implementation and should be overridden to provide
18684      * node-specific processing if necessary.
18685      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18686      * {@link #getTargetFromEvent} for this node)
18687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18688      * @param {Event} e The event
18689      * @param {Object} data An object containing arbitrary data supplied by the drag source
18690      */
18691     onNodeEnter : function(n, dd, e, data){
18692         
18693     },
18694
18695     /**
18696      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18697      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18698      * overridden to provide the proper feedback.
18699      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18700      * {@link #getTargetFromEvent} for this node)
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     onNodeOver : function(n, dd, e, data){
18708         return this.dropAllowed;
18709     },
18710
18711     /**
18712      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18713      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18714      * node-specific processing if necessary.
18715      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18716      * {@link #getTargetFromEvent} for this node)
18717      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18718      * @param {Event} e The event
18719      * @param {Object} data An object containing arbitrary data supplied by the drag source
18720      */
18721     onNodeOut : function(n, dd, e, data){
18722         
18723     },
18724
18725     /**
18726      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18727      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18728      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18729      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18730      * {@link #getTargetFromEvent} for this node)
18731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18732      * @param {Event} e The event
18733      * @param {Object} data An object containing arbitrary data supplied by the drag source
18734      * @return {Boolean} True if the drop was valid, else false
18735      */
18736     onNodeDrop : function(n, dd, e, data){
18737         return false;
18738     },
18739
18740     /**
18741      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18742      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18743      * it should be overridden to provide the proper feedback if necessary.
18744      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18745      * @param {Event} e The event
18746      * @param {Object} data An object containing arbitrary data supplied by the drag source
18747      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18748      * underlying {@link Roo.dd.StatusProxy} can be updated
18749      */
18750     onContainerOver : function(dd, e, data){
18751         return this.dropNotAllowed;
18752     },
18753
18754     /**
18755      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18756      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18757      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18758      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18759      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18760      * @param {Event} e The event
18761      * @param {Object} data An object containing arbitrary data supplied by the drag source
18762      * @return {Boolean} True if the drop was valid, else false
18763      */
18764     onContainerDrop : function(dd, e, data){
18765         return false;
18766     },
18767
18768     /**
18769      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18770      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18771      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18772      * you should override this method and provide a custom implementation.
18773      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18774      * @param {Event} e The event
18775      * @param {Object} data An object containing arbitrary data supplied by the drag source
18776      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18777      * underlying {@link Roo.dd.StatusProxy} can be updated
18778      */
18779     notifyEnter : function(dd, e, data){
18780         return this.dropNotAllowed;
18781     },
18782
18783     /**
18784      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18785      * This method will be called on every mouse movement while the drag source is over the drop zone.
18786      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18787      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18788      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18789      * registered node, it will call {@link #onContainerOver}.
18790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18791      * @param {Event} e The event
18792      * @param {Object} data An object containing arbitrary data supplied by the drag source
18793      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18794      * underlying {@link Roo.dd.StatusProxy} can be updated
18795      */
18796     notifyOver : function(dd, e, data){
18797         var n = this.getTargetFromEvent(e);
18798         if(!n){ // not over valid drop target
18799             if(this.lastOverNode){
18800                 this.onNodeOut(this.lastOverNode, dd, e, data);
18801                 this.lastOverNode = null;
18802             }
18803             return this.onContainerOver(dd, e, data);
18804         }
18805         if(this.lastOverNode != n){
18806             if(this.lastOverNode){
18807                 this.onNodeOut(this.lastOverNode, dd, e, data);
18808             }
18809             this.onNodeEnter(n, dd, e, data);
18810             this.lastOverNode = n;
18811         }
18812         return this.onNodeOver(n, dd, e, data);
18813     },
18814
18815     /**
18816      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18817      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18818      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18819      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18820      * @param {Event} e The event
18821      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18822      */
18823     notifyOut : function(dd, e, data){
18824         if(this.lastOverNode){
18825             this.onNodeOut(this.lastOverNode, dd, e, data);
18826             this.lastOverNode = null;
18827         }
18828     },
18829
18830     /**
18831      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18832      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18833      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18834      * otherwise it will call {@link #onContainerDrop}.
18835      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18836      * @param {Event} e The event
18837      * @param {Object} data An object containing arbitrary data supplied by the drag source
18838      * @return {Boolean} True if the drop was valid, else false
18839      */
18840     notifyDrop : function(dd, e, data){
18841         if(this.lastOverNode){
18842             this.onNodeOut(this.lastOverNode, dd, e, data);
18843             this.lastOverNode = null;
18844         }
18845         var n = this.getTargetFromEvent(e);
18846         return n ?
18847             this.onNodeDrop(n, dd, e, data) :
18848             this.onContainerDrop(dd, e, data);
18849     },
18850
18851     // private
18852     triggerCacheRefresh : function(){
18853         Roo.dd.DDM.refreshCache(this.groups);
18854     }  
18855 });/*
18856  * Based on:
18857  * Ext JS Library 1.1.1
18858  * Copyright(c) 2006-2007, Ext JS, LLC.
18859  *
18860  * Originally Released Under LGPL - original licence link has changed is not relivant.
18861  *
18862  * Fork - LGPL
18863  * <script type="text/javascript">
18864  */
18865
18866
18867 /**
18868  * @class Roo.data.SortTypes
18869  * @singleton
18870  * Defines the default sorting (casting?) comparison functions used when sorting data.
18871  */
18872 Roo.data.SortTypes = {
18873     /**
18874      * Default sort that does nothing
18875      * @param {Mixed} s The value being converted
18876      * @return {Mixed} The comparison value
18877      */
18878     none : function(s){
18879         return s;
18880     },
18881     
18882     /**
18883      * The regular expression used to strip tags
18884      * @type {RegExp}
18885      * @property
18886      */
18887     stripTagsRE : /<\/?[^>]+>/gi,
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asText : function(s){
18895         return String(s).replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Strips all HTML tags to sort on text only - Case insensitive
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCText : function(s){
18904         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18905     },
18906     
18907     /**
18908      * Case insensitive string
18909      * @param {Mixed} s The value being converted
18910      * @return {String} The comparison value
18911      */
18912     asUCString : function(s) {
18913         return String(s).toUpperCase();
18914     },
18915     
18916     /**
18917      * Date sorting
18918      * @param {Mixed} s The value being converted
18919      * @return {Number} The comparison value
18920      */
18921     asDate : function(s) {
18922         if(!s){
18923             return 0;
18924         }
18925         if(s instanceof Date){
18926             return s.getTime();
18927         }
18928         return Date.parse(String(s));
18929     },
18930     
18931     /**
18932      * Float sorting
18933      * @param {Mixed} s The value being converted
18934      * @return {Float} The comparison value
18935      */
18936     asFloat : function(s) {
18937         var val = parseFloat(String(s).replace(/,/g, ""));
18938         if(isNaN(val)) val = 0;
18939         return val;
18940     },
18941     
18942     /**
18943      * Integer sorting
18944      * @param {Mixed} s The value being converted
18945      * @return {Number} The comparison value
18946      */
18947     asInt : function(s) {
18948         var val = parseInt(String(s).replace(/,/g, ""));
18949         if(isNaN(val)) val = 0;
18950         return val;
18951     }
18952 };/*
18953  * Based on:
18954  * Ext JS Library 1.1.1
18955  * Copyright(c) 2006-2007, Ext JS, LLC.
18956  *
18957  * Originally Released Under LGPL - original licence link has changed is not relivant.
18958  *
18959  * Fork - LGPL
18960  * <script type="text/javascript">
18961  */
18962
18963 /**
18964 * @class Roo.data.Record
18965  * Instances of this class encapsulate both record <em>definition</em> information, and record
18966  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18967  * to access Records cached in an {@link Roo.data.Store} object.<br>
18968  * <p>
18969  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18970  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18971  * objects.<br>
18972  * <p>
18973  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18974  * @constructor
18975  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18976  * {@link #create}. The parameters are the same.
18977  * @param {Array} data An associative Array of data values keyed by the field name.
18978  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18979  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18980  * not specified an integer id is generated.
18981  */
18982 Roo.data.Record = function(data, id){
18983     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18984     this.data = data;
18985 };
18986
18987 /**
18988  * Generate a constructor for a specific record layout.
18989  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18990  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18991  * Each field definition object may contain the following properties: <ul>
18992  * <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,
18993  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18994  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18995  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18996  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18997  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18998  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18999  * this may be omitted.</p></li>
19000  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19001  * <ul><li>auto (Default, implies no conversion)</li>
19002  * <li>string</li>
19003  * <li>int</li>
19004  * <li>float</li>
19005  * <li>boolean</li>
19006  * <li>date</li></ul></p></li>
19007  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19008  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19009  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19010  * by the Reader into an object that will be stored in the Record. It is passed the
19011  * following parameters:<ul>
19012  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19013  * </ul></p></li>
19014  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19015  * </ul>
19016  * <br>usage:<br><pre><code>
19017 var TopicRecord = Roo.data.Record.create(
19018     {name: 'title', mapping: 'topic_title'},
19019     {name: 'author', mapping: 'username'},
19020     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19021     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19022     {name: 'lastPoster', mapping: 'user2'},
19023     {name: 'excerpt', mapping: 'post_text'}
19024 );
19025
19026 var myNewRecord = new TopicRecord({
19027     title: 'Do my job please',
19028     author: 'noobie',
19029     totalPosts: 1,
19030     lastPost: new Date(),
19031     lastPoster: 'Animal',
19032     excerpt: 'No way dude!'
19033 });
19034 myStore.add(myNewRecord);
19035 </code></pre>
19036  * @method create
19037  * @static
19038  */
19039 Roo.data.Record.create = function(o){
19040     var f = function(){
19041         f.superclass.constructor.apply(this, arguments);
19042     };
19043     Roo.extend(f, Roo.data.Record);
19044     var p = f.prototype;
19045     p.fields = new Roo.util.MixedCollection(false, function(field){
19046         return field.name;
19047     });
19048     for(var i = 0, len = o.length; i < len; i++){
19049         p.fields.add(new Roo.data.Field(o[i]));
19050     }
19051     f.getField = function(name){
19052         return p.fields.get(name);  
19053     };
19054     return f;
19055 };
19056
19057 Roo.data.Record.AUTO_ID = 1000;
19058 Roo.data.Record.EDIT = 'edit';
19059 Roo.data.Record.REJECT = 'reject';
19060 Roo.data.Record.COMMIT = 'commit';
19061
19062 Roo.data.Record.prototype = {
19063     /**
19064      * Readonly flag - true if this record has been modified.
19065      * @type Boolean
19066      */
19067     dirty : false,
19068     editing : false,
19069     error: null,
19070     modified: null,
19071
19072     // private
19073     join : function(store){
19074         this.store = store;
19075     },
19076
19077     /**
19078      * Set the named field to the specified value.
19079      * @param {String} name The name of the field to set.
19080      * @param {Object} value The value to set the field to.
19081      */
19082     set : function(name, value){
19083         if(this.data[name] == value){
19084             return;
19085         }
19086         this.dirty = true;
19087         if(!this.modified){
19088             this.modified = {};
19089         }
19090         if(typeof this.modified[name] == 'undefined'){
19091             this.modified[name] = this.data[name];
19092         }
19093         this.data[name] = value;
19094         if(!this.editing && this.store){
19095             this.store.afterEdit(this);
19096         }       
19097     },
19098
19099     /**
19100      * Get the value of the named field.
19101      * @param {String} name The name of the field to get the value of.
19102      * @return {Object} The value of the field.
19103      */
19104     get : function(name){
19105         return this.data[name]; 
19106     },
19107
19108     // private
19109     beginEdit : function(){
19110         this.editing = true;
19111         this.modified = {}; 
19112     },
19113
19114     // private
19115     cancelEdit : function(){
19116         this.editing = false;
19117         delete this.modified;
19118     },
19119
19120     // private
19121     endEdit : function(){
19122         this.editing = false;
19123         if(this.dirty && this.store){
19124             this.store.afterEdit(this);
19125         }
19126     },
19127
19128     /**
19129      * Usually called by the {@link Roo.data.Store} which owns the Record.
19130      * Rejects all changes made to the Record since either creation, or the last commit operation.
19131      * Modified fields are reverted to their original values.
19132      * <p>
19133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19134      * of reject operations.
19135      */
19136     reject : function(){
19137         var m = this.modified;
19138         for(var n in m){
19139             if(typeof m[n] != "function"){
19140                 this.data[n] = m[n];
19141             }
19142         }
19143         this.dirty = false;
19144         delete this.modified;
19145         this.editing = false;
19146         if(this.store){
19147             this.store.afterReject(this);
19148         }
19149     },
19150
19151     /**
19152      * Usually called by the {@link Roo.data.Store} which owns the Record.
19153      * Commits all changes made to the Record since either creation, or the last commit operation.
19154      * <p>
19155      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19156      * of commit operations.
19157      */
19158     commit : function(){
19159         this.dirty = false;
19160         delete this.modified;
19161         this.editing = false;
19162         if(this.store){
19163             this.store.afterCommit(this);
19164         }
19165     },
19166
19167     // private
19168     hasError : function(){
19169         return this.error != null;
19170     },
19171
19172     // private
19173     clearError : function(){
19174         this.error = null;
19175     },
19176
19177     /**
19178      * Creates a copy of this record.
19179      * @param {String} id (optional) A new record id if you don't want to use this record's id
19180      * @return {Record}
19181      */
19182     copy : function(newId) {
19183         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19184     }
19185 };/*
19186  * Based on:
19187  * Ext JS Library 1.1.1
19188  * Copyright(c) 2006-2007, Ext JS, LLC.
19189  *
19190  * Originally Released Under LGPL - original licence link has changed is not relivant.
19191  *
19192  * Fork - LGPL
19193  * <script type="text/javascript">
19194  */
19195
19196
19197
19198 /**
19199  * @class Roo.data.Store
19200  * @extends Roo.util.Observable
19201  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19202  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19203  * <p>
19204  * 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
19205  * has no knowledge of the format of the data returned by the Proxy.<br>
19206  * <p>
19207  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19208  * instances from the data object. These records are cached and made available through accessor functions.
19209  * @constructor
19210  * Creates a new Store.
19211  * @param {Object} config A config object containing the objects needed for the Store to access data,
19212  * and read the data into Records.
19213  */
19214 Roo.data.Store = function(config){
19215     this.data = new Roo.util.MixedCollection(false);
19216     this.data.getKey = function(o){
19217         return o.id;
19218     };
19219     this.baseParams = {};
19220     // private
19221     this.paramNames = {
19222         "start" : "start",
19223         "limit" : "limit",
19224         "sort" : "sort",
19225         "dir" : "dir",
19226         "multisort" : "_multisort"
19227     };
19228
19229     if(config && config.data){
19230         this.inlineData = config.data;
19231         delete config.data;
19232     }
19233
19234     Roo.apply(this, config);
19235     
19236     if(this.reader){ // reader passed
19237         this.reader = Roo.factory(this.reader, Roo.data);
19238         this.reader.xmodule = this.xmodule || false;
19239         if(!this.recordType){
19240             this.recordType = this.reader.recordType;
19241         }
19242         if(this.reader.onMetaChange){
19243             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19244         }
19245     }
19246
19247     if(this.recordType){
19248         this.fields = this.recordType.prototype.fields;
19249     }
19250     this.modified = [];
19251
19252     this.addEvents({
19253         /**
19254          * @event datachanged
19255          * Fires when the data cache has changed, and a widget which is using this Store
19256          * as a Record cache should refresh its view.
19257          * @param {Store} this
19258          */
19259         datachanged : true,
19260         /**
19261          * @event metachange
19262          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19263          * @param {Store} this
19264          * @param {Object} meta The JSON metadata
19265          */
19266         metachange : true,
19267         /**
19268          * @event add
19269          * Fires when Records have been added to the Store
19270          * @param {Store} this
19271          * @param {Roo.data.Record[]} records The array of Records added
19272          * @param {Number} index The index at which the record(s) were added
19273          */
19274         add : true,
19275         /**
19276          * @event remove
19277          * Fires when a Record has been removed from the Store
19278          * @param {Store} this
19279          * @param {Roo.data.Record} record The Record that was removed
19280          * @param {Number} index The index at which the record was removed
19281          */
19282         remove : true,
19283         /**
19284          * @event update
19285          * Fires when a Record has been updated
19286          * @param {Store} this
19287          * @param {Roo.data.Record} record The Record that was updated
19288          * @param {String} operation The update operation being performed.  Value may be one of:
19289          * <pre><code>
19290  Roo.data.Record.EDIT
19291  Roo.data.Record.REJECT
19292  Roo.data.Record.COMMIT
19293          * </code></pre>
19294          */
19295         update : true,
19296         /**
19297          * @event clear
19298          * Fires when the data cache has been cleared.
19299          * @param {Store} this
19300          */
19301         clear : true,
19302         /**
19303          * @event beforeload
19304          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19305          * the load action will be canceled.
19306          * @param {Store} this
19307          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19308          */
19309         beforeload : true,
19310         /**
19311          * @event load
19312          * Fires after a new set of Records has been loaded.
19313          * @param {Store} this
19314          * @param {Roo.data.Record[]} records The Records that were loaded
19315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19316          */
19317         load : true,
19318         /**
19319          * @event loadexception
19320          * Fires if an exception occurs in the Proxy during loading.
19321          * Called with the signature of the Proxy's "loadexception" event.
19322          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19323          * 
19324          * @param {Proxy} 
19325          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19326          * @param {Object} load options 
19327          * @param {Object} jsonData from your request (normally this contains the Exception)
19328          */
19329         loadexception : true
19330     });
19331     
19332     if(this.proxy){
19333         this.proxy = Roo.factory(this.proxy, Roo.data);
19334         this.proxy.xmodule = this.xmodule || false;
19335         this.relayEvents(this.proxy,  ["loadexception"]);
19336     }
19337     this.sortToggle = {};
19338     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19339
19340     Roo.data.Store.superclass.constructor.call(this);
19341
19342     if(this.inlineData){
19343         this.loadData(this.inlineData);
19344         delete this.inlineData;
19345     }
19346 };
19347 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19348      /**
19349     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19350     * without a remote query - used by combo/forms at present.
19351     */
19352     
19353     /**
19354     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19355     */
19356     /**
19357     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19358     */
19359     /**
19360     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19361     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19362     */
19363     /**
19364     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19365     * on any HTTP request
19366     */
19367     /**
19368     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19369     */
19370     /**
19371     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19372     */
19373     multiSort: false,
19374     /**
19375     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19376     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19377     */
19378     remoteSort : false,
19379
19380     /**
19381     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19382      * loaded or when a record is removed. (defaults to false).
19383     */
19384     pruneModifiedRecords : false,
19385
19386     // private
19387     lastOptions : null,
19388
19389     /**
19390      * Add Records to the Store and fires the add event.
19391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19392      */
19393     add : function(records){
19394         records = [].concat(records);
19395         for(var i = 0, len = records.length; i < len; i++){
19396             records[i].join(this);
19397         }
19398         var index = this.data.length;
19399         this.data.addAll(records);
19400         this.fireEvent("add", this, records, index);
19401     },
19402
19403     /**
19404      * Remove a Record from the Store and fires the remove event.
19405      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19406      */
19407     remove : function(record){
19408         var index = this.data.indexOf(record);
19409         this.data.removeAt(index);
19410         if(this.pruneModifiedRecords){
19411             this.modified.remove(record);
19412         }
19413         this.fireEvent("remove", this, record, index);
19414     },
19415
19416     /**
19417      * Remove all Records from the Store and fires the clear event.
19418      */
19419     removeAll : function(){
19420         this.data.clear();
19421         if(this.pruneModifiedRecords){
19422             this.modified = [];
19423         }
19424         this.fireEvent("clear", this);
19425     },
19426
19427     /**
19428      * Inserts Records to the Store at the given index and fires the add event.
19429      * @param {Number} index The start index at which to insert the passed Records.
19430      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19431      */
19432     insert : function(index, records){
19433         records = [].concat(records);
19434         for(var i = 0, len = records.length; i < len; i++){
19435             this.data.insert(index, records[i]);
19436             records[i].join(this);
19437         }
19438         this.fireEvent("add", this, records, index);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the passed Record.
19443      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19444      * @return {Number} The index of the passed Record. Returns -1 if not found.
19445      */
19446     indexOf : function(record){
19447         return this.data.indexOf(record);
19448     },
19449
19450     /**
19451      * Get the index within the cache of the Record with the passed id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Number} The index of the Record. Returns -1 if not found.
19454      */
19455     indexOfId : function(id){
19456         return this.data.indexOfKey(id);
19457     },
19458
19459     /**
19460      * Get the Record with the specified id.
19461      * @param {String} id The id of the Record to find.
19462      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19463      */
19464     getById : function(id){
19465         return this.data.key(id);
19466     },
19467
19468     /**
19469      * Get the Record at the specified index.
19470      * @param {Number} index The index of the Record to find.
19471      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19472      */
19473     getAt : function(index){
19474         return this.data.itemAt(index);
19475     },
19476
19477     /**
19478      * Returns a range of Records between specified indices.
19479      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19480      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19481      * @return {Roo.data.Record[]} An array of Records
19482      */
19483     getRange : function(start, end){
19484         return this.data.getRange(start, end);
19485     },
19486
19487     // private
19488     storeOptions : function(o){
19489         o = Roo.apply({}, o);
19490         delete o.callback;
19491         delete o.scope;
19492         this.lastOptions = o;
19493     },
19494
19495     /**
19496      * Loads the Record cache from the configured Proxy using the configured Reader.
19497      * <p>
19498      * If using remote paging, then the first load call must specify the <em>start</em>
19499      * and <em>limit</em> properties in the options.params property to establish the initial
19500      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19501      * <p>
19502      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19503      * and this call will return before the new data has been loaded. Perform any post-processing
19504      * in a callback function, or in a "load" event handler.</strong>
19505      * <p>
19506      * @param {Object} options An object containing properties which control loading options:<ul>
19507      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19508      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19509      * passed the following arguments:<ul>
19510      * <li>r : Roo.data.Record[]</li>
19511      * <li>options: Options object from the load call</li>
19512      * <li>success: Boolean success indicator</li></ul></li>
19513      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19514      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19515      * </ul>
19516      */
19517     load : function(options){
19518         options = options || {};
19519         if(this.fireEvent("beforeload", this, options) !== false){
19520             this.storeOptions(options);
19521             var p = Roo.apply(options.params || {}, this.baseParams);
19522             // if meta was not loaded from remote source.. try requesting it.
19523             if (!this.reader.metaFromRemote) {
19524                 p._requestMeta = 1;
19525             }
19526             if(this.sortInfo && this.remoteSort){
19527                 var pn = this.paramNames;
19528                 p[pn["sort"]] = this.sortInfo.field;
19529                 p[pn["dir"]] = this.sortInfo.direction;
19530             }
19531             if (this.multiSort) {
19532                 var pn = this.paramNames;
19533                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19534             }
19535             
19536             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19537         }
19538     },
19539
19540     /**
19541      * Reloads the Record cache from the configured Proxy using the configured Reader and
19542      * the options from the last load operation performed.
19543      * @param {Object} options (optional) An object containing properties which may override the options
19544      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19545      * the most recently used options are reused).
19546      */
19547     reload : function(options){
19548         this.load(Roo.applyIf(options||{}, this.lastOptions));
19549     },
19550
19551     // private
19552     // Called as a callback by the Reader during a load operation.
19553     loadRecords : function(o, options, success){
19554         if(!o || success === false){
19555             if(success !== false){
19556                 this.fireEvent("load", this, [], options);
19557             }
19558             if(options.callback){
19559                 options.callback.call(options.scope || this, [], options, false);
19560             }
19561             return;
19562         }
19563         // if data returned failure - throw an exception.
19564         if (o.success === false) {
19565             // show a message if no listener is registered.
19566             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19567                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19568             }
19569             // loadmask wil be hooked into this..
19570             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19571             return;
19572         }
19573         var r = o.records, t = o.totalRecords || r.length;
19574         if(!options || options.add !== true){
19575             if(this.pruneModifiedRecords){
19576                 this.modified = [];
19577             }
19578             for(var i = 0, len = r.length; i < len; i++){
19579                 r[i].join(this);
19580             }
19581             if(this.snapshot){
19582                 this.data = this.snapshot;
19583                 delete this.snapshot;
19584             }
19585             this.data.clear();
19586             this.data.addAll(r);
19587             this.totalLength = t;
19588             this.applySort();
19589             this.fireEvent("datachanged", this);
19590         }else{
19591             this.totalLength = Math.max(t, this.data.length+r.length);
19592             this.add(r);
19593         }
19594         this.fireEvent("load", this, r, options);
19595         if(options.callback){
19596             options.callback.call(options.scope || this, r, options, true);
19597         }
19598     },
19599
19600
19601     /**
19602      * Loads data from a passed data block. A Reader which understands the format of the data
19603      * must have been configured in the constructor.
19604      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19605      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19606      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19607      */
19608     loadData : function(o, append){
19609         var r = this.reader.readRecords(o);
19610         this.loadRecords(r, {add: append}, true);
19611     },
19612
19613     /**
19614      * Gets the number of cached records.
19615      * <p>
19616      * <em>If using paging, this may not be the total size of the dataset. If the data object
19617      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19618      * the data set size</em>
19619      */
19620     getCount : function(){
19621         return this.data.length || 0;
19622     },
19623
19624     /**
19625      * Gets the total number of records in the dataset as returned by the server.
19626      * <p>
19627      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19628      * the dataset size</em>
19629      */
19630     getTotalCount : function(){
19631         return this.totalLength || 0;
19632     },
19633
19634     /**
19635      * Returns the sort state of the Store as an object with two properties:
19636      * <pre><code>
19637  field {String} The name of the field by which the Records are sorted
19638  direction {String} The sort order, "ASC" or "DESC"
19639      * </code></pre>
19640      */
19641     getSortState : function(){
19642         return this.sortInfo;
19643     },
19644
19645     // private
19646     applySort : function(){
19647         if(this.sortInfo && !this.remoteSort){
19648             var s = this.sortInfo, f = s.field;
19649             var st = this.fields.get(f).sortType;
19650             var fn = function(r1, r2){
19651                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19652                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19653             };
19654             this.data.sort(s.direction, fn);
19655             if(this.snapshot && this.snapshot != this.data){
19656                 this.snapshot.sort(s.direction, fn);
19657             }
19658         }
19659     },
19660
19661     /**
19662      * Sets the default sort column and order to be used by the next load operation.
19663      * @param {String} fieldName The name of the field to sort by.
19664      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19665      */
19666     setDefaultSort : function(field, dir){
19667         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19668     },
19669
19670     /**
19671      * Sort the Records.
19672      * If remote sorting is used, the sort is performed on the server, and the cache is
19673      * reloaded. If local sorting is used, the cache is sorted internally.
19674      * @param {String} fieldName The name of the field to sort by.
19675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19676      */
19677     sort : function(fieldName, dir){
19678         var f = this.fields.get(fieldName);
19679         if(!dir){
19680             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19681             
19682             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19683                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19684             }else{
19685                 dir = f.sortDir;
19686             }
19687         }
19688         this.sortToggle[f.name] = dir;
19689         this.sortInfo = {field: f.name, direction: dir};
19690         if(!this.remoteSort){
19691             this.applySort();
19692             this.fireEvent("datachanged", this);
19693         }else{
19694             this.load(this.lastOptions);
19695         }
19696     },
19697
19698     /**
19699      * Calls the specified function for each of the Records in the cache.
19700      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19701      * Returning <em>false</em> aborts and exits the iteration.
19702      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19703      */
19704     each : function(fn, scope){
19705         this.data.each(fn, scope);
19706     },
19707
19708     /**
19709      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19710      * (e.g., during paging).
19711      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19712      */
19713     getModifiedRecords : function(){
19714         return this.modified;
19715     },
19716
19717     // private
19718     createFilterFn : function(property, value, anyMatch){
19719         if(!value.exec){ // not a regex
19720             value = String(value);
19721             if(value.length == 0){
19722                 return false;
19723             }
19724             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19725         }
19726         return function(r){
19727             return value.test(r.data[property]);
19728         };
19729     },
19730
19731     /**
19732      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19733      * @param {String} property A field on your records
19734      * @param {Number} start The record index to start at (defaults to 0)
19735      * @param {Number} end The last record index to include (defaults to length - 1)
19736      * @return {Number} The sum
19737      */
19738     sum : function(property, start, end){
19739         var rs = this.data.items, v = 0;
19740         start = start || 0;
19741         end = (end || end === 0) ? end : rs.length-1;
19742
19743         for(var i = start; i <= end; i++){
19744             v += (rs[i].data[property] || 0);
19745         }
19746         return v;
19747     },
19748
19749     /**
19750      * Filter the records by a specified property.
19751      * @param {String} field A field on your records
19752      * @param {String/RegExp} value Either a string that the field
19753      * should start with or a RegExp to test against the field
19754      * @param {Boolean} anyMatch True to match any part not just the beginning
19755      */
19756     filter : function(property, value, anyMatch){
19757         var fn = this.createFilterFn(property, value, anyMatch);
19758         return fn ? this.filterBy(fn) : this.clearFilter();
19759     },
19760
19761     /**
19762      * Filter by a function. The specified function will be called with each
19763      * record in this data source. If the function returns true the record is included,
19764      * otherwise it is filtered.
19765      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19766      * @param {Object} scope (optional) The scope of the function (defaults to this)
19767      */
19768     filterBy : function(fn, scope){
19769         this.snapshot = this.snapshot || this.data;
19770         this.data = this.queryBy(fn, scope||this);
19771         this.fireEvent("datachanged", this);
19772     },
19773
19774     /**
19775      * Query the records by a specified property.
19776      * @param {String} field A field on your records
19777      * @param {String/RegExp} value Either a string that the field
19778      * should start with or a RegExp to test against the field
19779      * @param {Boolean} anyMatch True to match any part not just the beginning
19780      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19781      */
19782     query : function(property, value, anyMatch){
19783         var fn = this.createFilterFn(property, value, anyMatch);
19784         return fn ? this.queryBy(fn) : this.data.clone();
19785     },
19786
19787     /**
19788      * Query by a function. The specified function will be called with each
19789      * record in this data source. If the function returns true the record is included
19790      * in the results.
19791      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19792      * @param {Object} scope (optional) The scope of the function (defaults to this)
19793       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19794      **/
19795     queryBy : function(fn, scope){
19796         var data = this.snapshot || this.data;
19797         return data.filterBy(fn, scope||this);
19798     },
19799
19800     /**
19801      * Collects unique values for a particular dataIndex from this store.
19802      * @param {String} dataIndex The property to collect
19803      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19804      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19805      * @return {Array} An array of the unique values
19806      **/
19807     collect : function(dataIndex, allowNull, bypassFilter){
19808         var d = (bypassFilter === true && this.snapshot) ?
19809                 this.snapshot.items : this.data.items;
19810         var v, sv, r = [], l = {};
19811         for(var i = 0, len = d.length; i < len; i++){
19812             v = d[i].data[dataIndex];
19813             sv = String(v);
19814             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19815                 l[sv] = true;
19816                 r[r.length] = v;
19817             }
19818         }
19819         return r;
19820     },
19821
19822     /**
19823      * Revert to a view of the Record cache with no filtering applied.
19824      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19825      */
19826     clearFilter : function(suppressEvent){
19827         if(this.snapshot && this.snapshot != this.data){
19828             this.data = this.snapshot;
19829             delete this.snapshot;
19830             if(suppressEvent !== true){
19831                 this.fireEvent("datachanged", this);
19832             }
19833         }
19834     },
19835
19836     // private
19837     afterEdit : function(record){
19838         if(this.modified.indexOf(record) == -1){
19839             this.modified.push(record);
19840         }
19841         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19842     },
19843     
19844     // private
19845     afterReject : function(record){
19846         this.modified.remove(record);
19847         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19848     },
19849
19850     // private
19851     afterCommit : function(record){
19852         this.modified.remove(record);
19853         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19854     },
19855
19856     /**
19857      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19858      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19859      */
19860     commitChanges : function(){
19861         var m = this.modified.slice(0);
19862         this.modified = [];
19863         for(var i = 0, len = m.length; i < len; i++){
19864             m[i].commit();
19865         }
19866     },
19867
19868     /**
19869      * Cancel outstanding changes on all changed records.
19870      */
19871     rejectChanges : function(){
19872         var m = this.modified.slice(0);
19873         this.modified = [];
19874         for(var i = 0, len = m.length; i < len; i++){
19875             m[i].reject();
19876         }
19877     },
19878
19879     onMetaChange : function(meta, rtype, o){
19880         this.recordType = rtype;
19881         this.fields = rtype.prototype.fields;
19882         delete this.snapshot;
19883         this.sortInfo = meta.sortInfo || this.sortInfo;
19884         this.modified = [];
19885         this.fireEvent('metachange', this, this.reader.meta);
19886     }
19887 });/*
19888  * Based on:
19889  * Ext JS Library 1.1.1
19890  * Copyright(c) 2006-2007, Ext JS, LLC.
19891  *
19892  * Originally Released Under LGPL - original licence link has changed is not relivant.
19893  *
19894  * Fork - LGPL
19895  * <script type="text/javascript">
19896  */
19897
19898 /**
19899  * @class Roo.data.SimpleStore
19900  * @extends Roo.data.Store
19901  * Small helper class to make creating Stores from Array data easier.
19902  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19903  * @cfg {Array} fields An array of field definition objects, or field name strings.
19904  * @cfg {Array} data The multi-dimensional array of data
19905  * @constructor
19906  * @param {Object} config
19907  */
19908 Roo.data.SimpleStore = function(config){
19909     Roo.data.SimpleStore.superclass.constructor.call(this, {
19910         isLocal : true,
19911         reader: new Roo.data.ArrayReader({
19912                 id: config.id
19913             },
19914             Roo.data.Record.create(config.fields)
19915         ),
19916         proxy : new Roo.data.MemoryProxy(config.data)
19917     });
19918     this.load();
19919 };
19920 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19921  * Based on:
19922  * Ext JS Library 1.1.1
19923  * Copyright(c) 2006-2007, Ext JS, LLC.
19924  *
19925  * Originally Released Under LGPL - original licence link has changed is not relivant.
19926  *
19927  * Fork - LGPL
19928  * <script type="text/javascript">
19929  */
19930
19931 /**
19932 /**
19933  * @extends Roo.data.Store
19934  * @class Roo.data.JsonStore
19935  * Small helper class to make creating Stores for JSON data easier. <br/>
19936 <pre><code>
19937 var store = new Roo.data.JsonStore({
19938     url: 'get-images.php',
19939     root: 'images',
19940     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19941 });
19942 </code></pre>
19943  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19944  * JsonReader and HttpProxy (unless inline data is provided).</b>
19945  * @cfg {Array} fields An array of field definition objects, or field name strings.
19946  * @constructor
19947  * @param {Object} config
19948  */
19949 Roo.data.JsonStore = function(c){
19950     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19951         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19952         reader: new Roo.data.JsonReader(c, c.fields)
19953     }));
19954 };
19955 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19956  * Based on:
19957  * Ext JS Library 1.1.1
19958  * Copyright(c) 2006-2007, Ext JS, LLC.
19959  *
19960  * Originally Released Under LGPL - original licence link has changed is not relivant.
19961  *
19962  * Fork - LGPL
19963  * <script type="text/javascript">
19964  */
19965
19966  
19967 Roo.data.Field = function(config){
19968     if(typeof config == "string"){
19969         config = {name: config};
19970     }
19971     Roo.apply(this, config);
19972     
19973     if(!this.type){
19974         this.type = "auto";
19975     }
19976     
19977     var st = Roo.data.SortTypes;
19978     // named sortTypes are supported, here we look them up
19979     if(typeof this.sortType == "string"){
19980         this.sortType = st[this.sortType];
19981     }
19982     
19983     // set default sortType for strings and dates
19984     if(!this.sortType){
19985         switch(this.type){
19986             case "string":
19987                 this.sortType = st.asUCString;
19988                 break;
19989             case "date":
19990                 this.sortType = st.asDate;
19991                 break;
19992             default:
19993                 this.sortType = st.none;
19994         }
19995     }
19996
19997     // define once
19998     var stripRe = /[\$,%]/g;
19999
20000     // prebuilt conversion function for this field, instead of
20001     // switching every time we're reading a value
20002     if(!this.convert){
20003         var cv, dateFormat = this.dateFormat;
20004         switch(this.type){
20005             case "":
20006             case "auto":
20007             case undefined:
20008                 cv = function(v){ return v; };
20009                 break;
20010             case "string":
20011                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20012                 break;
20013             case "int":
20014                 cv = function(v){
20015                     return v !== undefined && v !== null && v !== '' ?
20016                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20017                     };
20018                 break;
20019             case "float":
20020                 cv = function(v){
20021                     return v !== undefined && v !== null && v !== '' ?
20022                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20023                     };
20024                 break;
20025             case "bool":
20026             case "boolean":
20027                 cv = function(v){ return v === true || v === "true" || v == 1; };
20028                 break;
20029             case "date":
20030                 cv = function(v){
20031                     if(!v){
20032                         return '';
20033                     }
20034                     if(v instanceof Date){
20035                         return v;
20036                     }
20037                     if(dateFormat){
20038                         if(dateFormat == "timestamp"){
20039                             return new Date(v*1000);
20040                         }
20041                         return Date.parseDate(v, dateFormat);
20042                     }
20043                     var parsed = Date.parse(v);
20044                     return parsed ? new Date(parsed) : null;
20045                 };
20046              break;
20047             
20048         }
20049         this.convert = cv;
20050     }
20051 };
20052
20053 Roo.data.Field.prototype = {
20054     dateFormat: null,
20055     defaultValue: "",
20056     mapping: null,
20057     sortType : null,
20058     sortDir : "ASC"
20059 };/*
20060  * Based on:
20061  * Ext JS Library 1.1.1
20062  * Copyright(c) 2006-2007, Ext JS, LLC.
20063  *
20064  * Originally Released Under LGPL - original licence link has changed is not relivant.
20065  *
20066  * Fork - LGPL
20067  * <script type="text/javascript">
20068  */
20069  
20070 // Base class for reading structured data from a data source.  This class is intended to be
20071 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20072
20073 /**
20074  * @class Roo.data.DataReader
20075  * Base class for reading structured data from a data source.  This class is intended to be
20076  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20077  */
20078
20079 Roo.data.DataReader = function(meta, recordType){
20080     
20081     this.meta = meta;
20082     
20083     this.recordType = recordType instanceof Array ? 
20084         Roo.data.Record.create(recordType) : recordType;
20085 };
20086
20087 Roo.data.DataReader.prototype = {
20088      /**
20089      * Create an empty record
20090      * @param {Object} data (optional) - overlay some values
20091      * @return {Roo.data.Record} record created.
20092      */
20093     newRow :  function(d) {
20094         var da =  {};
20095         this.recordType.prototype.fields.each(function(c) {
20096             switch( c.type) {
20097                 case 'int' : da[c.name] = 0; break;
20098                 case 'date' : da[c.name] = new Date(); break;
20099                 case 'float' : da[c.name] = 0.0; break;
20100                 case 'boolean' : da[c.name] = false; break;
20101                 default : da[c.name] = ""; break;
20102             }
20103             
20104         });
20105         return new this.recordType(Roo.apply(da, d));
20106     }
20107     
20108 };/*
20109  * Based on:
20110  * Ext JS Library 1.1.1
20111  * Copyright(c) 2006-2007, Ext JS, LLC.
20112  *
20113  * Originally Released Under LGPL - original licence link has changed is not relivant.
20114  *
20115  * Fork - LGPL
20116  * <script type="text/javascript">
20117  */
20118
20119 /**
20120  * @class Roo.data.DataProxy
20121  * @extends Roo.data.Observable
20122  * This class is an abstract base class for implementations which provide retrieval of
20123  * unformatted data objects.<br>
20124  * <p>
20125  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20126  * (of the appropriate type which knows how to parse the data object) to provide a block of
20127  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20128  * <p>
20129  * Custom implementations must implement the load method as described in
20130  * {@link Roo.data.HttpProxy#load}.
20131  */
20132 Roo.data.DataProxy = function(){
20133     this.addEvents({
20134         /**
20135          * @event beforeload
20136          * Fires before a network request is made to retrieve a data object.
20137          * @param {Object} This DataProxy object.
20138          * @param {Object} params The params parameter to the load function.
20139          */
20140         beforeload : true,
20141         /**
20142          * @event load
20143          * Fires before the load method's callback is called.
20144          * @param {Object} This DataProxy object.
20145          * @param {Object} o The data object.
20146          * @param {Object} arg The callback argument object passed to the load function.
20147          */
20148         load : true,
20149         /**
20150          * @event loadexception
20151          * Fires if an Exception occurs during data retrieval.
20152          * @param {Object} This DataProxy object.
20153          * @param {Object} o The data object.
20154          * @param {Object} arg The callback argument object passed to the load function.
20155          * @param {Object} e The Exception.
20156          */
20157         loadexception : true
20158     });
20159     Roo.data.DataProxy.superclass.constructor.call(this);
20160 };
20161
20162 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20163
20164     /**
20165      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20166      */
20167 /*
20168  * Based on:
20169  * Ext JS Library 1.1.1
20170  * Copyright(c) 2006-2007, Ext JS, LLC.
20171  *
20172  * Originally Released Under LGPL - original licence link has changed is not relivant.
20173  *
20174  * Fork - LGPL
20175  * <script type="text/javascript">
20176  */
20177 /**
20178  * @class Roo.data.MemoryProxy
20179  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20180  * to the Reader when its load method is called.
20181  * @constructor
20182  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20183  */
20184 Roo.data.MemoryProxy = function(data){
20185     if (data.data) {
20186         data = data.data;
20187     }
20188     Roo.data.MemoryProxy.superclass.constructor.call(this);
20189     this.data = data;
20190 };
20191
20192 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20193     /**
20194      * Load data from the requested source (in this case an in-memory
20195      * data object passed to the constructor), read the data object into
20196      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20197      * process that block using the passed callback.
20198      * @param {Object} params This parameter is not used by the MemoryProxy class.
20199      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20200      * object into a block of Roo.data.Records.
20201      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20202      * The function must be passed <ul>
20203      * <li>The Record block object</li>
20204      * <li>The "arg" argument from the load function</li>
20205      * <li>A boolean success indicator</li>
20206      * </ul>
20207      * @param {Object} scope The scope in which to call the callback
20208      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20209      */
20210     load : function(params, reader, callback, scope, arg){
20211         params = params || {};
20212         var result;
20213         try {
20214             result = reader.readRecords(this.data);
20215         }catch(e){
20216             this.fireEvent("loadexception", this, arg, null, e);
20217             callback.call(scope, null, arg, false);
20218             return;
20219         }
20220         callback.call(scope, result, arg, true);
20221     },
20222     
20223     // private
20224     update : function(params, records){
20225         
20226     }
20227 });/*
20228  * Based on:
20229  * Ext JS Library 1.1.1
20230  * Copyright(c) 2006-2007, Ext JS, LLC.
20231  *
20232  * Originally Released Under LGPL - original licence link has changed is not relivant.
20233  *
20234  * Fork - LGPL
20235  * <script type="text/javascript">
20236  */
20237 /**
20238  * @class Roo.data.HttpProxy
20239  * @extends Roo.data.DataProxy
20240  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20241  * configured to reference a certain URL.<br><br>
20242  * <p>
20243  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20244  * from which the running page was served.<br><br>
20245  * <p>
20246  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20247  * <p>
20248  * Be aware that to enable the browser to parse an XML document, the server must set
20249  * the Content-Type header in the HTTP response to "text/xml".
20250  * @constructor
20251  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20252  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20253  * will be used to make the request.
20254  */
20255 Roo.data.HttpProxy = function(conn){
20256     Roo.data.HttpProxy.superclass.constructor.call(this);
20257     // is conn a conn config or a real conn?
20258     this.conn = conn;
20259     this.useAjax = !conn || !conn.events;
20260   
20261 };
20262
20263 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20264     // thse are take from connection...
20265     
20266     /**
20267      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20268      */
20269     /**
20270      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20271      * extra parameters to each request made by this object. (defaults to undefined)
20272      */
20273     /**
20274      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20275      *  to each request made by this object. (defaults to undefined)
20276      */
20277     /**
20278      * @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)
20279      */
20280     /**
20281      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20282      */
20283      /**
20284      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20285      * @type Boolean
20286      */
20287   
20288
20289     /**
20290      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20291      * @type Boolean
20292      */
20293     /**
20294      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20295      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20296      * a finer-grained basis than the DataProxy events.
20297      */
20298     getConnection : function(){
20299         return this.useAjax ? Roo.Ajax : this.conn;
20300     },
20301
20302     /**
20303      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20304      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20305      * process that block using the passed callback.
20306      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20307      * for the request to the remote server.
20308      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20309      * object into a block of Roo.data.Records.
20310      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20311      * The function must be passed <ul>
20312      * <li>The Record block object</li>
20313      * <li>The "arg" argument from the load function</li>
20314      * <li>A boolean success indicator</li>
20315      * </ul>
20316      * @param {Object} scope The scope in which to call the callback
20317      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20318      */
20319     load : function(params, reader, callback, scope, arg){
20320         if(this.fireEvent("beforeload", this, params) !== false){
20321             var  o = {
20322                 params : params || {},
20323                 request: {
20324                     callback : callback,
20325                     scope : scope,
20326                     arg : arg
20327                 },
20328                 reader: reader,
20329                 callback : this.loadResponse,
20330                 scope: this
20331             };
20332             if(this.useAjax){
20333                 Roo.applyIf(o, this.conn);
20334                 if(this.activeRequest){
20335                     Roo.Ajax.abort(this.activeRequest);
20336                 }
20337                 this.activeRequest = Roo.Ajax.request(o);
20338             }else{
20339                 this.conn.request(o);
20340             }
20341         }else{
20342             callback.call(scope||this, null, arg, false);
20343         }
20344     },
20345
20346     // private
20347     loadResponse : function(o, success, response){
20348         delete this.activeRequest;
20349         if(!success){
20350             this.fireEvent("loadexception", this, o, response);
20351             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20352             return;
20353         }
20354         var result;
20355         try {
20356             result = o.reader.read(response);
20357         }catch(e){
20358             this.fireEvent("loadexception", this, o, response, e);
20359             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20360             return;
20361         }
20362         
20363         this.fireEvent("load", this, o, o.request.arg);
20364         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20365     },
20366
20367     // private
20368     update : function(dataSet){
20369
20370     },
20371
20372     // private
20373     updateResponse : function(dataSet){
20374
20375     }
20376 });/*
20377  * Based on:
20378  * Ext JS Library 1.1.1
20379  * Copyright(c) 2006-2007, Ext JS, LLC.
20380  *
20381  * Originally Released Under LGPL - original licence link has changed is not relivant.
20382  *
20383  * Fork - LGPL
20384  * <script type="text/javascript">
20385  */
20386
20387 /**
20388  * @class Roo.data.ScriptTagProxy
20389  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20390  * other than the originating domain of the running page.<br><br>
20391  * <p>
20392  * <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
20393  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20394  * <p>
20395  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20396  * source code that is used as the source inside a &lt;script> tag.<br><br>
20397  * <p>
20398  * In order for the browser to process the returned data, the server must wrap the data object
20399  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20400  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20401  * depending on whether the callback name was passed:
20402  * <p>
20403  * <pre><code>
20404 boolean scriptTag = false;
20405 String cb = request.getParameter("callback");
20406 if (cb != null) {
20407     scriptTag = true;
20408     response.setContentType("text/javascript");
20409 } else {
20410     response.setContentType("application/x-json");
20411 }
20412 Writer out = response.getWriter();
20413 if (scriptTag) {
20414     out.write(cb + "(");
20415 }
20416 out.print(dataBlock.toJsonString());
20417 if (scriptTag) {
20418     out.write(");");
20419 }
20420 </pre></code>
20421  *
20422  * @constructor
20423  * @param {Object} config A configuration object.
20424  */
20425 Roo.data.ScriptTagProxy = function(config){
20426     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20427     Roo.apply(this, config);
20428     this.head = document.getElementsByTagName("head")[0];
20429 };
20430
20431 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20432
20433 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20434     /**
20435      * @cfg {String} url The URL from which to request the data object.
20436      */
20437     /**
20438      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20439      */
20440     timeout : 30000,
20441     /**
20442      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20443      * the server the name of the callback function set up by the load call to process the returned data object.
20444      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20445      * javascript output which calls this named function passing the data object as its only parameter.
20446      */
20447     callbackParam : "callback",
20448     /**
20449      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20450      * name to the request.
20451      */
20452     nocache : true,
20453
20454     /**
20455      * Load data from the configured URL, read the data object into
20456      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20457      * process that block using the passed callback.
20458      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20459      * for the request to the remote server.
20460      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20461      * object into a block of Roo.data.Records.
20462      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20463      * The function must be passed <ul>
20464      * <li>The Record block object</li>
20465      * <li>The "arg" argument from the load function</li>
20466      * <li>A boolean success indicator</li>
20467      * </ul>
20468      * @param {Object} scope The scope in which to call the callback
20469      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20470      */
20471     load : function(params, reader, callback, scope, arg){
20472         if(this.fireEvent("beforeload", this, params) !== false){
20473
20474             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20475
20476             var url = this.url;
20477             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20478             if(this.nocache){
20479                 url += "&_dc=" + (new Date().getTime());
20480             }
20481             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20482             var trans = {
20483                 id : transId,
20484                 cb : "stcCallback"+transId,
20485                 scriptId : "stcScript"+transId,
20486                 params : params,
20487                 arg : arg,
20488                 url : url,
20489                 callback : callback,
20490                 scope : scope,
20491                 reader : reader
20492             };
20493             var conn = this;
20494
20495             window[trans.cb] = function(o){
20496                 conn.handleResponse(o, trans);
20497             };
20498
20499             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20500
20501             if(this.autoAbort !== false){
20502                 this.abort();
20503             }
20504
20505             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20506
20507             var script = document.createElement("script");
20508             script.setAttribute("src", url);
20509             script.setAttribute("type", "text/javascript");
20510             script.setAttribute("id", trans.scriptId);
20511             this.head.appendChild(script);
20512
20513             this.trans = trans;
20514         }else{
20515             callback.call(scope||this, null, arg, false);
20516         }
20517     },
20518
20519     // private
20520     isLoading : function(){
20521         return this.trans ? true : false;
20522     },
20523
20524     /**
20525      * Abort the current server request.
20526      */
20527     abort : function(){
20528         if(this.isLoading()){
20529             this.destroyTrans(this.trans);
20530         }
20531     },
20532
20533     // private
20534     destroyTrans : function(trans, isLoaded){
20535         this.head.removeChild(document.getElementById(trans.scriptId));
20536         clearTimeout(trans.timeoutId);
20537         if(isLoaded){
20538             window[trans.cb] = undefined;
20539             try{
20540                 delete window[trans.cb];
20541             }catch(e){}
20542         }else{
20543             // if hasn't been loaded, wait for load to remove it to prevent script error
20544             window[trans.cb] = function(){
20545                 window[trans.cb] = undefined;
20546                 try{
20547                     delete window[trans.cb];
20548                 }catch(e){}
20549             };
20550         }
20551     },
20552
20553     // private
20554     handleResponse : function(o, trans){
20555         this.trans = false;
20556         this.destroyTrans(trans, true);
20557         var result;
20558         try {
20559             result = trans.reader.readRecords(o);
20560         }catch(e){
20561             this.fireEvent("loadexception", this, o, trans.arg, e);
20562             trans.callback.call(trans.scope||window, null, trans.arg, false);
20563             return;
20564         }
20565         this.fireEvent("load", this, o, trans.arg);
20566         trans.callback.call(trans.scope||window, result, trans.arg, true);
20567     },
20568
20569     // private
20570     handleFailure : function(trans){
20571         this.trans = false;
20572         this.destroyTrans(trans, false);
20573         this.fireEvent("loadexception", this, null, trans.arg);
20574         trans.callback.call(trans.scope||window, null, trans.arg, false);
20575     }
20576 });/*
20577  * Based on:
20578  * Ext JS Library 1.1.1
20579  * Copyright(c) 2006-2007, Ext JS, LLC.
20580  *
20581  * Originally Released Under LGPL - original licence link has changed is not relivant.
20582  *
20583  * Fork - LGPL
20584  * <script type="text/javascript">
20585  */
20586
20587 /**
20588  * @class Roo.data.JsonReader
20589  * @extends Roo.data.DataReader
20590  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20591  * based on mappings in a provided Roo.data.Record constructor.
20592  * 
20593  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20594  * in the reply previously. 
20595  * 
20596  * <p>
20597  * Example code:
20598  * <pre><code>
20599 var RecordDef = Roo.data.Record.create([
20600     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20601     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20602 ]);
20603 var myReader = new Roo.data.JsonReader({
20604     totalProperty: "results",    // The property which contains the total dataset size (optional)
20605     root: "rows",                // The property which contains an Array of row objects
20606     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20607 }, RecordDef);
20608 </code></pre>
20609  * <p>
20610  * This would consume a JSON file like this:
20611  * <pre><code>
20612 { 'results': 2, 'rows': [
20613     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20614     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20615 }
20616 </code></pre>
20617  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20618  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20619  * paged from the remote server.
20620  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20621  * @cfg {String} root name of the property which contains the Array of row objects.
20622  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20623  * @constructor
20624  * Create a new JsonReader
20625  * @param {Object} meta Metadata configuration options
20626  * @param {Object} recordType Either an Array of field definition objects,
20627  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20628  */
20629 Roo.data.JsonReader = function(meta, recordType){
20630     
20631     meta = meta || {};
20632     // set some defaults:
20633     Roo.applyIf(meta, {
20634         totalProperty: 'total',
20635         successProperty : 'success',
20636         root : 'data',
20637         id : 'id'
20638     });
20639     
20640     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20641 };
20642 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20643     
20644     /**
20645      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20646      * Used by Store query builder to append _requestMeta to params.
20647      * 
20648      */
20649     metaFromRemote : false,
20650     /**
20651      * This method is only used by a DataProxy which has retrieved data from a remote server.
20652      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20653      * @return {Object} data A data block which is used by an Roo.data.Store object as
20654      * a cache of Roo.data.Records.
20655      */
20656     read : function(response){
20657         var json = response.responseText;
20658        
20659         var o = /* eval:var:o */ eval("("+json+")");
20660         if(!o) {
20661             throw {message: "JsonReader.read: Json object not found"};
20662         }
20663         
20664         if(o.metaData){
20665             
20666             delete this.ef;
20667             this.metaFromRemote = true;
20668             this.meta = o.metaData;
20669             this.recordType = Roo.data.Record.create(o.metaData.fields);
20670             this.onMetaChange(this.meta, this.recordType, o);
20671         }
20672         return this.readRecords(o);
20673     },
20674
20675     // private function a store will implement
20676     onMetaChange : function(meta, recordType, o){
20677
20678     },
20679
20680     /**
20681          * @ignore
20682          */
20683     simpleAccess: function(obj, subsc) {
20684         return obj[subsc];
20685     },
20686
20687         /**
20688          * @ignore
20689          */
20690     getJsonAccessor: function(){
20691         var re = /[\[\.]/;
20692         return function(expr) {
20693             try {
20694                 return(re.test(expr))
20695                     ? new Function("obj", "return obj." + expr)
20696                     : function(obj){
20697                         return obj[expr];
20698                     };
20699             } catch(e){}
20700             return Roo.emptyFn;
20701         };
20702     }(),
20703
20704     /**
20705      * Create a data block containing Roo.data.Records from an XML document.
20706      * @param {Object} o An object which contains an Array of row objects in the property specified
20707      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20708      * which contains the total size of the dataset.
20709      * @return {Object} data A data block which is used by an Roo.data.Store object as
20710      * a cache of Roo.data.Records.
20711      */
20712     readRecords : function(o){
20713         /**
20714          * After any data loads, the raw JSON data is available for further custom processing.
20715          * @type Object
20716          */
20717         this.jsonData = o;
20718         var s = this.meta, Record = this.recordType,
20719             f = Record.prototype.fields, fi = f.items, fl = f.length;
20720
20721 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20722         if (!this.ef) {
20723             if(s.totalProperty) {
20724                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20725                 }
20726                 if(s.successProperty) {
20727                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20728                 }
20729                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20730                 if (s.id) {
20731                         var g = this.getJsonAccessor(s.id);
20732                         this.getId = function(rec) {
20733                                 var r = g(rec);
20734                                 return (r === undefined || r === "") ? null : r;
20735                         };
20736                 } else {
20737                         this.getId = function(){return null;};
20738                 }
20739             this.ef = [];
20740             for(var jj = 0; jj < fl; jj++){
20741                 f = fi[jj];
20742                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20743                 this.ef[jj] = this.getJsonAccessor(map);
20744             }
20745         }
20746
20747         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20748         if(s.totalProperty){
20749             var vt = parseInt(this.getTotal(o), 10);
20750             if(!isNaN(vt)){
20751                 totalRecords = vt;
20752             }
20753         }
20754         if(s.successProperty){
20755             var vs = this.getSuccess(o);
20756             if(vs === false || vs === 'false'){
20757                 success = false;
20758             }
20759         }
20760         var records = [];
20761             for(var i = 0; i < c; i++){
20762                     var n = root[i];
20763                 var values = {};
20764                 var id = this.getId(n);
20765                 for(var j = 0; j < fl; j++){
20766                     f = fi[j];
20767                 var v = this.ef[j](n);
20768                 if (!f.convert) {
20769                     Roo.log('missing convert for ' + f.name);
20770                     Roo.log(f);
20771                     continue;
20772                 }
20773                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20774                 }
20775                 var record = new Record(values, id);
20776                 record.json = n;
20777                 records[i] = record;
20778             }
20779             return {
20780                 success : success,
20781                 records : records,
20782                 totalRecords : totalRecords
20783             };
20784     }
20785 });/*
20786  * Based on:
20787  * Ext JS Library 1.1.1
20788  * Copyright(c) 2006-2007, Ext JS, LLC.
20789  *
20790  * Originally Released Under LGPL - original licence link has changed is not relivant.
20791  *
20792  * Fork - LGPL
20793  * <script type="text/javascript">
20794  */
20795
20796 /**
20797  * @class Roo.data.XmlReader
20798  * @extends Roo.data.DataReader
20799  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20800  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20801  * <p>
20802  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20803  * header in the HTTP response must be set to "text/xml".</em>
20804  * <p>
20805  * Example code:
20806  * <pre><code>
20807 var RecordDef = Roo.data.Record.create([
20808    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20809    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20810 ]);
20811 var myReader = new Roo.data.XmlReader({
20812    totalRecords: "results", // The element which contains the total dataset size (optional)
20813    record: "row",           // The repeated element which contains row information
20814    id: "id"                 // The element within the row that provides an ID for the record (optional)
20815 }, RecordDef);
20816 </code></pre>
20817  * <p>
20818  * This would consume an XML file like this:
20819  * <pre><code>
20820 &lt;?xml?>
20821 &lt;dataset>
20822  &lt;results>2&lt;/results>
20823  &lt;row>
20824    &lt;id>1&lt;/id>
20825    &lt;name>Bill&lt;/name>
20826    &lt;occupation>Gardener&lt;/occupation>
20827  &lt;/row>
20828  &lt;row>
20829    &lt;id>2&lt;/id>
20830    &lt;name>Ben&lt;/name>
20831    &lt;occupation>Horticulturalist&lt;/occupation>
20832  &lt;/row>
20833 &lt;/dataset>
20834 </code></pre>
20835  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20836  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20837  * paged from the remote server.
20838  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20839  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20840  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20841  * a record identifier value.
20842  * @constructor
20843  * Create a new XmlReader
20844  * @param {Object} meta Metadata configuration options
20845  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20846  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20847  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20848  */
20849 Roo.data.XmlReader = function(meta, recordType){
20850     meta = meta || {};
20851     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20852 };
20853 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20854     /**
20855      * This method is only used by a DataProxy which has retrieved data from a remote server.
20856          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20857          * to contain a method called 'responseXML' that returns an XML document object.
20858      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20859      * a cache of Roo.data.Records.
20860      */
20861     read : function(response){
20862         var doc = response.responseXML;
20863         if(!doc) {
20864             throw {message: "XmlReader.read: XML Document not available"};
20865         }
20866         return this.readRecords(doc);
20867     },
20868
20869     /**
20870      * Create a data block containing Roo.data.Records from an XML document.
20871          * @param {Object} doc A parsed XML document.
20872      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20873      * a cache of Roo.data.Records.
20874      */
20875     readRecords : function(doc){
20876         /**
20877          * After any data loads/reads, the raw XML Document is available for further custom processing.
20878          * @type XMLDocument
20879          */
20880         this.xmlData = doc;
20881         var root = doc.documentElement || doc;
20882         var q = Roo.DomQuery;
20883         var recordType = this.recordType, fields = recordType.prototype.fields;
20884         var sid = this.meta.id;
20885         var totalRecords = 0, success = true;
20886         if(this.meta.totalRecords){
20887             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20888         }
20889         
20890         if(this.meta.success){
20891             var sv = q.selectValue(this.meta.success, root, true);
20892             success = sv !== false && sv !== 'false';
20893         }
20894         var records = [];
20895         var ns = q.select(this.meta.record, root);
20896         for(var i = 0, len = ns.length; i < len; i++) {
20897                 var n = ns[i];
20898                 var values = {};
20899                 var id = sid ? q.selectValue(sid, n) : undefined;
20900                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20901                     var f = fields.items[j];
20902                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20903                     v = f.convert(v);
20904                     values[f.name] = v;
20905                 }
20906                 var record = new recordType(values, id);
20907                 record.node = n;
20908                 records[records.length] = record;
20909             }
20910
20911             return {
20912                 success : success,
20913                 records : records,
20914                 totalRecords : totalRecords || records.length
20915             };
20916     }
20917 });/*
20918  * Based on:
20919  * Ext JS Library 1.1.1
20920  * Copyright(c) 2006-2007, Ext JS, LLC.
20921  *
20922  * Originally Released Under LGPL - original licence link has changed is not relivant.
20923  *
20924  * Fork - LGPL
20925  * <script type="text/javascript">
20926  */
20927
20928 /**
20929  * @class Roo.data.ArrayReader
20930  * @extends Roo.data.DataReader
20931  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20932  * Each element of that Array represents a row of data fields. The
20933  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20934  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20935  * <p>
20936  * Example code:.
20937  * <pre><code>
20938 var RecordDef = Roo.data.Record.create([
20939     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20940     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20941 ]);
20942 var myReader = new Roo.data.ArrayReader({
20943     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20944 }, RecordDef);
20945 </code></pre>
20946  * <p>
20947  * This would consume an Array like this:
20948  * <pre><code>
20949 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20950   </code></pre>
20951  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20952  * @constructor
20953  * Create a new JsonReader
20954  * @param {Object} meta Metadata configuration options.
20955  * @param {Object} recordType Either an Array of field definition objects
20956  * as specified to {@link Roo.data.Record#create},
20957  * or an {@link Roo.data.Record} object
20958  * created using {@link Roo.data.Record#create}.
20959  */
20960 Roo.data.ArrayReader = function(meta, recordType){
20961     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20962 };
20963
20964 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20965     /**
20966      * Create a data block containing Roo.data.Records from an XML document.
20967      * @param {Object} o An Array of row objects which represents the dataset.
20968      * @return {Object} data A data block which is used by an Roo.data.Store object as
20969      * a cache of Roo.data.Records.
20970      */
20971     readRecords : function(o){
20972         var sid = this.meta ? this.meta.id : null;
20973         var recordType = this.recordType, fields = recordType.prototype.fields;
20974         var records = [];
20975         var root = o;
20976             for(var i = 0; i < root.length; i++){
20977                     var n = root[i];
20978                 var values = {};
20979                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20980                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20981                 var f = fields.items[j];
20982                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20983                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20984                 v = f.convert(v);
20985                 values[f.name] = v;
20986             }
20987                 var record = new recordType(values, id);
20988                 record.json = n;
20989                 records[records.length] = record;
20990             }
20991             return {
20992                 records : records,
20993                 totalRecords : records.length
20994             };
20995     }
20996 });/*
20997  * Based on:
20998  * Ext JS Library 1.1.1
20999  * Copyright(c) 2006-2007, Ext JS, LLC.
21000  *
21001  * Originally Released Under LGPL - original licence link has changed is not relivant.
21002  *
21003  * Fork - LGPL
21004  * <script type="text/javascript">
21005  */
21006
21007
21008 /**
21009  * @class Roo.data.Tree
21010  * @extends Roo.util.Observable
21011  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21012  * in the tree have most standard DOM functionality.
21013  * @constructor
21014  * @param {Node} root (optional) The root node
21015  */
21016 Roo.data.Tree = function(root){
21017    this.nodeHash = {};
21018    /**
21019     * The root node for this tree
21020     * @type Node
21021     */
21022    this.root = null;
21023    if(root){
21024        this.setRootNode(root);
21025    }
21026    this.addEvents({
21027        /**
21028         * @event append
21029         * Fires when a new child node is appended to a node in this tree.
21030         * @param {Tree} tree The owner tree
21031         * @param {Node} parent The parent node
21032         * @param {Node} node The newly appended node
21033         * @param {Number} index The index of the newly appended node
21034         */
21035        "append" : true,
21036        /**
21037         * @event remove
21038         * Fires when a child node is removed from a node in this tree.
21039         * @param {Tree} tree The owner tree
21040         * @param {Node} parent The parent node
21041         * @param {Node} node The child node removed
21042         */
21043        "remove" : true,
21044        /**
21045         * @event move
21046         * Fires when a node is moved to a new location in the tree
21047         * @param {Tree} tree The owner tree
21048         * @param {Node} node The node moved
21049         * @param {Node} oldParent The old parent of this node
21050         * @param {Node} newParent The new parent of this node
21051         * @param {Number} index The index it was moved to
21052         */
21053        "move" : true,
21054        /**
21055         * @event insert
21056         * Fires when a new child node is inserted in a node in this tree.
21057         * @param {Tree} tree The owner tree
21058         * @param {Node} parent The parent node
21059         * @param {Node} node The child node inserted
21060         * @param {Node} refNode The child node the node was inserted before
21061         */
21062        "insert" : true,
21063        /**
21064         * @event beforeappend
21065         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21066         * @param {Tree} tree The owner tree
21067         * @param {Node} parent The parent node
21068         * @param {Node} node The child node to be appended
21069         */
21070        "beforeappend" : true,
21071        /**
21072         * @event beforeremove
21073         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21074         * @param {Tree} tree The owner tree
21075         * @param {Node} parent The parent node
21076         * @param {Node} node The child node to be removed
21077         */
21078        "beforeremove" : true,
21079        /**
21080         * @event beforemove
21081         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21082         * @param {Tree} tree The owner tree
21083         * @param {Node} node The node being moved
21084         * @param {Node} oldParent The parent of the node
21085         * @param {Node} newParent The new parent the node is moving to
21086         * @param {Number} index The index it is being moved to
21087         */
21088        "beforemove" : true,
21089        /**
21090         * @event beforeinsert
21091         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21092         * @param {Tree} tree The owner tree
21093         * @param {Node} parent The parent node
21094         * @param {Node} node The child node to be inserted
21095         * @param {Node} refNode The child node the node is being inserted before
21096         */
21097        "beforeinsert" : true
21098    });
21099
21100     Roo.data.Tree.superclass.constructor.call(this);
21101 };
21102
21103 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21104     pathSeparator: "/",
21105
21106     proxyNodeEvent : function(){
21107         return this.fireEvent.apply(this, arguments);
21108     },
21109
21110     /**
21111      * Returns the root node for this tree.
21112      * @return {Node}
21113      */
21114     getRootNode : function(){
21115         return this.root;
21116     },
21117
21118     /**
21119      * Sets the root node for this tree.
21120      * @param {Node} node
21121      * @return {Node}
21122      */
21123     setRootNode : function(node){
21124         this.root = node;
21125         node.ownerTree = this;
21126         node.isRoot = true;
21127         this.registerNode(node);
21128         return node;
21129     },
21130
21131     /**
21132      * Gets a node in this tree by its id.
21133      * @param {String} id
21134      * @return {Node}
21135      */
21136     getNodeById : function(id){
21137         return this.nodeHash[id];
21138     },
21139
21140     registerNode : function(node){
21141         this.nodeHash[node.id] = node;
21142     },
21143
21144     unregisterNode : function(node){
21145         delete this.nodeHash[node.id];
21146     },
21147
21148     toString : function(){
21149         return "[Tree"+(this.id?" "+this.id:"")+"]";
21150     }
21151 });
21152
21153 /**
21154  * @class Roo.data.Node
21155  * @extends Roo.util.Observable
21156  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21157  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21158  * @constructor
21159  * @param {Object} attributes The attributes/config for the node
21160  */
21161 Roo.data.Node = function(attributes){
21162     /**
21163      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21164      * @type {Object}
21165      */
21166     this.attributes = attributes || {};
21167     this.leaf = this.attributes.leaf;
21168     /**
21169      * The node id. @type String
21170      */
21171     this.id = this.attributes.id;
21172     if(!this.id){
21173         this.id = Roo.id(null, "ynode-");
21174         this.attributes.id = this.id;
21175     }
21176     /**
21177      * All child nodes of this node. @type Array
21178      */
21179     this.childNodes = [];
21180     if(!this.childNodes.indexOf){ // indexOf is a must
21181         this.childNodes.indexOf = function(o){
21182             for(var i = 0, len = this.length; i < len; i++){
21183                 if(this[i] == o) {
21184                     return i;
21185                 }
21186             }
21187             return -1;
21188         };
21189     }
21190     /**
21191      * The parent node for this node. @type Node
21192      */
21193     this.parentNode = null;
21194     /**
21195      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21196      */
21197     this.firstChild = null;
21198     /**
21199      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21200      */
21201     this.lastChild = null;
21202     /**
21203      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21204      */
21205     this.previousSibling = null;
21206     /**
21207      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21208      */
21209     this.nextSibling = null;
21210
21211     this.addEvents({
21212        /**
21213         * @event append
21214         * Fires when a new child node is appended
21215         * @param {Tree} tree The owner tree
21216         * @param {Node} this This node
21217         * @param {Node} node The newly appended node
21218         * @param {Number} index The index of the newly appended node
21219         */
21220        "append" : true,
21221        /**
21222         * @event remove
21223         * Fires when a child node is removed
21224         * @param {Tree} tree The owner tree
21225         * @param {Node} this This node
21226         * @param {Node} node The removed node
21227         */
21228        "remove" : true,
21229        /**
21230         * @event move
21231         * Fires when this node is moved to a new location in the tree
21232         * @param {Tree} tree The owner tree
21233         * @param {Node} this This node
21234         * @param {Node} oldParent The old parent of this node
21235         * @param {Node} newParent The new parent of this node
21236         * @param {Number} index The index it was moved to
21237         */
21238        "move" : true,
21239        /**
21240         * @event insert
21241         * Fires when a new child node is inserted.
21242         * @param {Tree} tree The owner tree
21243         * @param {Node} this This node
21244         * @param {Node} node The child node inserted
21245         * @param {Node} refNode The child node the node was inserted before
21246         */
21247        "insert" : true,
21248        /**
21249         * @event beforeappend
21250         * Fires before a new child is appended, return false to cancel the append.
21251         * @param {Tree} tree The owner tree
21252         * @param {Node} this This node
21253         * @param {Node} node The child node to be appended
21254         */
21255        "beforeappend" : true,
21256        /**
21257         * @event beforeremove
21258         * Fires before a child is removed, return false to cancel the remove.
21259         * @param {Tree} tree The owner tree
21260         * @param {Node} this This node
21261         * @param {Node} node The child node to be removed
21262         */
21263        "beforeremove" : true,
21264        /**
21265         * @event beforemove
21266         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21267         * @param {Tree} tree The owner tree
21268         * @param {Node} this This node
21269         * @param {Node} oldParent The parent of this node
21270         * @param {Node} newParent The new parent this node is moving to
21271         * @param {Number} index The index it is being moved to
21272         */
21273        "beforemove" : true,
21274        /**
21275         * @event beforeinsert
21276         * Fires before a new child is inserted, return false to cancel the insert.
21277         * @param {Tree} tree The owner tree
21278         * @param {Node} this This node
21279         * @param {Node} node The child node to be inserted
21280         * @param {Node} refNode The child node the node is being inserted before
21281         */
21282        "beforeinsert" : true
21283    });
21284     this.listeners = this.attributes.listeners;
21285     Roo.data.Node.superclass.constructor.call(this);
21286 };
21287
21288 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21289     fireEvent : function(evtName){
21290         // first do standard event for this node
21291         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21292             return false;
21293         }
21294         // then bubble it up to the tree if the event wasn't cancelled
21295         var ot = this.getOwnerTree();
21296         if(ot){
21297             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21298                 return false;
21299             }
21300         }
21301         return true;
21302     },
21303
21304     /**
21305      * Returns true if this node is a leaf
21306      * @return {Boolean}
21307      */
21308     isLeaf : function(){
21309         return this.leaf === true;
21310     },
21311
21312     // private
21313     setFirstChild : function(node){
21314         this.firstChild = node;
21315     },
21316
21317     //private
21318     setLastChild : function(node){
21319         this.lastChild = node;
21320     },
21321
21322
21323     /**
21324      * Returns true if this node is the last child of its parent
21325      * @return {Boolean}
21326      */
21327     isLast : function(){
21328        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21329     },
21330
21331     /**
21332      * Returns true if this node is the first child of its parent
21333      * @return {Boolean}
21334      */
21335     isFirst : function(){
21336        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21337     },
21338
21339     hasChildNodes : function(){
21340         return !this.isLeaf() && this.childNodes.length > 0;
21341     },
21342
21343     /**
21344      * Insert node(s) as the last child node of this node.
21345      * @param {Node/Array} node The node or Array of nodes to append
21346      * @return {Node} The appended node if single append, or null if an array was passed
21347      */
21348     appendChild : function(node){
21349         var multi = false;
21350         if(node instanceof Array){
21351             multi = node;
21352         }else if(arguments.length > 1){
21353             multi = arguments;
21354         }
21355         // if passed an array or multiple args do them one by one
21356         if(multi){
21357             for(var i = 0, len = multi.length; i < len; i++) {
21358                 this.appendChild(multi[i]);
21359             }
21360         }else{
21361             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21362                 return false;
21363             }
21364             var index = this.childNodes.length;
21365             var oldParent = node.parentNode;
21366             // it's a move, make sure we move it cleanly
21367             if(oldParent){
21368                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21369                     return false;
21370                 }
21371                 oldParent.removeChild(node);
21372             }
21373             index = this.childNodes.length;
21374             if(index == 0){
21375                 this.setFirstChild(node);
21376             }
21377             this.childNodes.push(node);
21378             node.parentNode = this;
21379             var ps = this.childNodes[index-1];
21380             if(ps){
21381                 node.previousSibling = ps;
21382                 ps.nextSibling = node;
21383             }else{
21384                 node.previousSibling = null;
21385             }
21386             node.nextSibling = null;
21387             this.setLastChild(node);
21388             node.setOwnerTree(this.getOwnerTree());
21389             this.fireEvent("append", this.ownerTree, this, node, index);
21390             if(oldParent){
21391                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21392             }
21393             return node;
21394         }
21395     },
21396
21397     /**
21398      * Removes a child node from this node.
21399      * @param {Node} node The node to remove
21400      * @return {Node} The removed node
21401      */
21402     removeChild : function(node){
21403         var index = this.childNodes.indexOf(node);
21404         if(index == -1){
21405             return false;
21406         }
21407         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21408             return false;
21409         }
21410
21411         // remove it from childNodes collection
21412         this.childNodes.splice(index, 1);
21413
21414         // update siblings
21415         if(node.previousSibling){
21416             node.previousSibling.nextSibling = node.nextSibling;
21417         }
21418         if(node.nextSibling){
21419             node.nextSibling.previousSibling = node.previousSibling;
21420         }
21421
21422         // update child refs
21423         if(this.firstChild == node){
21424             this.setFirstChild(node.nextSibling);
21425         }
21426         if(this.lastChild == node){
21427             this.setLastChild(node.previousSibling);
21428         }
21429
21430         node.setOwnerTree(null);
21431         // clear any references from the node
21432         node.parentNode = null;
21433         node.previousSibling = null;
21434         node.nextSibling = null;
21435         this.fireEvent("remove", this.ownerTree, this, node);
21436         return node;
21437     },
21438
21439     /**
21440      * Inserts the first node before the second node in this nodes childNodes collection.
21441      * @param {Node} node The node to insert
21442      * @param {Node} refNode The node to insert before (if null the node is appended)
21443      * @return {Node} The inserted node
21444      */
21445     insertBefore : function(node, refNode){
21446         if(!refNode){ // like standard Dom, refNode can be null for append
21447             return this.appendChild(node);
21448         }
21449         // nothing to do
21450         if(node == refNode){
21451             return false;
21452         }
21453
21454         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21455             return false;
21456         }
21457         var index = this.childNodes.indexOf(refNode);
21458         var oldParent = node.parentNode;
21459         var refIndex = index;
21460
21461         // when moving internally, indexes will change after remove
21462         if(oldParent == this && this.childNodes.indexOf(node) < index){
21463             refIndex--;
21464         }
21465
21466         // it's a move, make sure we move it cleanly
21467         if(oldParent){
21468             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21469                 return false;
21470             }
21471             oldParent.removeChild(node);
21472         }
21473         if(refIndex == 0){
21474             this.setFirstChild(node);
21475         }
21476         this.childNodes.splice(refIndex, 0, node);
21477         node.parentNode = this;
21478         var ps = this.childNodes[refIndex-1];
21479         if(ps){
21480             node.previousSibling = ps;
21481             ps.nextSibling = node;
21482         }else{
21483             node.previousSibling = null;
21484         }
21485         node.nextSibling = refNode;
21486         refNode.previousSibling = node;
21487         node.setOwnerTree(this.getOwnerTree());
21488         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21489         if(oldParent){
21490             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21491         }
21492         return node;
21493     },
21494
21495     /**
21496      * Returns the child node at the specified index.
21497      * @param {Number} index
21498      * @return {Node}
21499      */
21500     item : function(index){
21501         return this.childNodes[index];
21502     },
21503
21504     /**
21505      * Replaces one child node in this node with another.
21506      * @param {Node} newChild The replacement node
21507      * @param {Node} oldChild The node to replace
21508      * @return {Node} The replaced node
21509      */
21510     replaceChild : function(newChild, oldChild){
21511         this.insertBefore(newChild, oldChild);
21512         this.removeChild(oldChild);
21513         return oldChild;
21514     },
21515
21516     /**
21517      * Returns the index of a child node
21518      * @param {Node} node
21519      * @return {Number} The index of the node or -1 if it was not found
21520      */
21521     indexOf : function(child){
21522         return this.childNodes.indexOf(child);
21523     },
21524
21525     /**
21526      * Returns the tree this node is in.
21527      * @return {Tree}
21528      */
21529     getOwnerTree : function(){
21530         // if it doesn't have one, look for one
21531         if(!this.ownerTree){
21532             var p = this;
21533             while(p){
21534                 if(p.ownerTree){
21535                     this.ownerTree = p.ownerTree;
21536                     break;
21537                 }
21538                 p = p.parentNode;
21539             }
21540         }
21541         return this.ownerTree;
21542     },
21543
21544     /**
21545      * Returns depth of this node (the root node has a depth of 0)
21546      * @return {Number}
21547      */
21548     getDepth : function(){
21549         var depth = 0;
21550         var p = this;
21551         while(p.parentNode){
21552             ++depth;
21553             p = p.parentNode;
21554         }
21555         return depth;
21556     },
21557
21558     // private
21559     setOwnerTree : function(tree){
21560         // if it's move, we need to update everyone
21561         if(tree != this.ownerTree){
21562             if(this.ownerTree){
21563                 this.ownerTree.unregisterNode(this);
21564             }
21565             this.ownerTree = tree;
21566             var cs = this.childNodes;
21567             for(var i = 0, len = cs.length; i < len; i++) {
21568                 cs[i].setOwnerTree(tree);
21569             }
21570             if(tree){
21571                 tree.registerNode(this);
21572             }
21573         }
21574     },
21575
21576     /**
21577      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21578      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21579      * @return {String} The path
21580      */
21581     getPath : function(attr){
21582         attr = attr || "id";
21583         var p = this.parentNode;
21584         var b = [this.attributes[attr]];
21585         while(p){
21586             b.unshift(p.attributes[attr]);
21587             p = p.parentNode;
21588         }
21589         var sep = this.getOwnerTree().pathSeparator;
21590         return sep + b.join(sep);
21591     },
21592
21593     /**
21594      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21595      * function call will be the scope provided or the current node. The arguments to the function
21596      * will be the args provided or the current node. If the function returns false at any point,
21597      * the bubble is stopped.
21598      * @param {Function} fn The function to call
21599      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21600      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21601      */
21602     bubble : function(fn, scope, args){
21603         var p = this;
21604         while(p){
21605             if(fn.call(scope || p, args || p) === false){
21606                 break;
21607             }
21608             p = p.parentNode;
21609         }
21610     },
21611
21612     /**
21613      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21614      * function call will be the scope provided or the current node. The arguments to the function
21615      * will be the args provided or the current node. If the function returns false at any point,
21616      * the cascade is stopped on that branch.
21617      * @param {Function} fn The function to call
21618      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21619      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21620      */
21621     cascade : function(fn, scope, args){
21622         if(fn.call(scope || this, args || this) !== false){
21623             var cs = this.childNodes;
21624             for(var i = 0, len = cs.length; i < len; i++) {
21625                 cs[i].cascade(fn, scope, args);
21626             }
21627         }
21628     },
21629
21630     /**
21631      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21632      * function call will be the scope provided or the current node. The arguments to the function
21633      * will be the args provided or the current node. If the function returns false at any point,
21634      * the iteration stops.
21635      * @param {Function} fn The function to call
21636      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21637      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21638      */
21639     eachChild : function(fn, scope, args){
21640         var cs = this.childNodes;
21641         for(var i = 0, len = cs.length; i < len; i++) {
21642                 if(fn.call(scope || this, args || cs[i]) === false){
21643                     break;
21644                 }
21645         }
21646     },
21647
21648     /**
21649      * Finds the first child that has the attribute with the specified value.
21650      * @param {String} attribute The attribute name
21651      * @param {Mixed} value The value to search for
21652      * @return {Node} The found child or null if none was found
21653      */
21654     findChild : function(attribute, value){
21655         var cs = this.childNodes;
21656         for(var i = 0, len = cs.length; i < len; i++) {
21657                 if(cs[i].attributes[attribute] == value){
21658                     return cs[i];
21659                 }
21660         }
21661         return null;
21662     },
21663
21664     /**
21665      * Finds the first child by a custom function. The child matches if the function passed
21666      * returns true.
21667      * @param {Function} fn
21668      * @param {Object} scope (optional)
21669      * @return {Node} The found child or null if none was found
21670      */
21671     findChildBy : function(fn, scope){
21672         var cs = this.childNodes;
21673         for(var i = 0, len = cs.length; i < len; i++) {
21674                 if(fn.call(scope||cs[i], cs[i]) === true){
21675                     return cs[i];
21676                 }
21677         }
21678         return null;
21679     },
21680
21681     /**
21682      * Sorts this nodes children using the supplied sort function
21683      * @param {Function} fn
21684      * @param {Object} scope (optional)
21685      */
21686     sort : function(fn, scope){
21687         var cs = this.childNodes;
21688         var len = cs.length;
21689         if(len > 0){
21690             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21691             cs.sort(sortFn);
21692             for(var i = 0; i < len; i++){
21693                 var n = cs[i];
21694                 n.previousSibling = cs[i-1];
21695                 n.nextSibling = cs[i+1];
21696                 if(i == 0){
21697                     this.setFirstChild(n);
21698                 }
21699                 if(i == len-1){
21700                     this.setLastChild(n);
21701                 }
21702             }
21703         }
21704     },
21705
21706     /**
21707      * Returns true if this node is an ancestor (at any point) of the passed node.
21708      * @param {Node} node
21709      * @return {Boolean}
21710      */
21711     contains : function(node){
21712         return node.isAncestor(this);
21713     },
21714
21715     /**
21716      * Returns true if the passed node is an ancestor (at any point) of this node.
21717      * @param {Node} node
21718      * @return {Boolean}
21719      */
21720     isAncestor : function(node){
21721         var p = this.parentNode;
21722         while(p){
21723             if(p == node){
21724                 return true;
21725             }
21726             p = p.parentNode;
21727         }
21728         return false;
21729     },
21730
21731     toString : function(){
21732         return "[Node"+(this.id?" "+this.id:"")+"]";
21733     }
21734 });/*
21735  * Based on:
21736  * Ext JS Library 1.1.1
21737  * Copyright(c) 2006-2007, Ext JS, LLC.
21738  *
21739  * Originally Released Under LGPL - original licence link has changed is not relivant.
21740  *
21741  * Fork - LGPL
21742  * <script type="text/javascript">
21743  */
21744  
21745
21746 /**
21747  * @class Roo.ComponentMgr
21748  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21749  * @singleton
21750  */
21751 Roo.ComponentMgr = function(){
21752     var all = new Roo.util.MixedCollection();
21753
21754     return {
21755         /**
21756          * Registers a component.
21757          * @param {Roo.Component} c The component
21758          */
21759         register : function(c){
21760             all.add(c);
21761         },
21762
21763         /**
21764          * Unregisters a component.
21765          * @param {Roo.Component} c The component
21766          */
21767         unregister : function(c){
21768             all.remove(c);
21769         },
21770
21771         /**
21772          * Returns a component by id
21773          * @param {String} id The component id
21774          */
21775         get : function(id){
21776             return all.get(id);
21777         },
21778
21779         /**
21780          * Registers a function that will be called when a specified component is added to ComponentMgr
21781          * @param {String} id The component id
21782          * @param {Funtction} fn The callback function
21783          * @param {Object} scope The scope of the callback
21784          */
21785         onAvailable : function(id, fn, scope){
21786             all.on("add", function(index, o){
21787                 if(o.id == id){
21788                     fn.call(scope || o, o);
21789                     all.un("add", fn, scope);
21790                 }
21791             });
21792         }
21793     };
21794 }();/*
21795  * Based on:
21796  * Ext JS Library 1.1.1
21797  * Copyright(c) 2006-2007, Ext JS, LLC.
21798  *
21799  * Originally Released Under LGPL - original licence link has changed is not relivant.
21800  *
21801  * Fork - LGPL
21802  * <script type="text/javascript">
21803  */
21804  
21805 /**
21806  * @class Roo.Component
21807  * @extends Roo.util.Observable
21808  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21809  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21810  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21811  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21812  * All visual components (widgets) that require rendering into a layout should subclass Component.
21813  * @constructor
21814  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21815  * 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
21816  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21817  */
21818 Roo.Component = function(config){
21819     config = config || {};
21820     if(config.tagName || config.dom || typeof config == "string"){ // element object
21821         config = {el: config, id: config.id || config};
21822     }
21823     this.initialConfig = config;
21824
21825     Roo.apply(this, config);
21826     this.addEvents({
21827         /**
21828          * @event disable
21829          * Fires after the component is disabled.
21830              * @param {Roo.Component} this
21831              */
21832         disable : true,
21833         /**
21834          * @event enable
21835          * Fires after the component is enabled.
21836              * @param {Roo.Component} this
21837              */
21838         enable : true,
21839         /**
21840          * @event beforeshow
21841          * Fires before the component is shown.  Return false to stop the show.
21842              * @param {Roo.Component} this
21843              */
21844         beforeshow : true,
21845         /**
21846          * @event show
21847          * Fires after the component is shown.
21848              * @param {Roo.Component} this
21849              */
21850         show : true,
21851         /**
21852          * @event beforehide
21853          * Fires before the component is hidden. Return false to stop the hide.
21854              * @param {Roo.Component} this
21855              */
21856         beforehide : true,
21857         /**
21858          * @event hide
21859          * Fires after the component is hidden.
21860              * @param {Roo.Component} this
21861              */
21862         hide : true,
21863         /**
21864          * @event beforerender
21865          * Fires before the component is rendered. Return false to stop the render.
21866              * @param {Roo.Component} this
21867              */
21868         beforerender : true,
21869         /**
21870          * @event render
21871          * Fires after the component is rendered.
21872              * @param {Roo.Component} this
21873              */
21874         render : true,
21875         /**
21876          * @event beforedestroy
21877          * Fires before the component is destroyed. Return false to stop the destroy.
21878              * @param {Roo.Component} this
21879              */
21880         beforedestroy : true,
21881         /**
21882          * @event destroy
21883          * Fires after the component is destroyed.
21884              * @param {Roo.Component} this
21885              */
21886         destroy : true
21887     });
21888     if(!this.id){
21889         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21890     }
21891     Roo.ComponentMgr.register(this);
21892     Roo.Component.superclass.constructor.call(this);
21893     this.initComponent();
21894     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21895         this.render(this.renderTo);
21896         delete this.renderTo;
21897     }
21898 };
21899
21900 /** @private */
21901 Roo.Component.AUTO_ID = 1000;
21902
21903 Roo.extend(Roo.Component, Roo.util.Observable, {
21904     /**
21905      * @scope Roo.Component.prototype
21906      * @type {Boolean}
21907      * true if this component is hidden. Read-only.
21908      */
21909     hidden : false,
21910     /**
21911      * @type {Boolean}
21912      * true if this component is disabled. Read-only.
21913      */
21914     disabled : false,
21915     /**
21916      * @type {Boolean}
21917      * true if this component has been rendered. Read-only.
21918      */
21919     rendered : false,
21920     
21921     /** @cfg {String} disableClass
21922      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21923      */
21924     disabledClass : "x-item-disabled",
21925         /** @cfg {Boolean} allowDomMove
21926          * Whether the component can move the Dom node when rendering (defaults to true).
21927          */
21928     allowDomMove : true,
21929     /** @cfg {String} hideMode
21930      * How this component should hidden. Supported values are
21931      * "visibility" (css visibility), "offsets" (negative offset position) and
21932      * "display" (css display) - defaults to "display".
21933      */
21934     hideMode: 'display',
21935
21936     /** @private */
21937     ctype : "Roo.Component",
21938
21939     /**
21940      * @cfg {String} actionMode 
21941      * which property holds the element that used for  hide() / show() / disable() / enable()
21942      * default is 'el' 
21943      */
21944     actionMode : "el",
21945
21946     /** @private */
21947     getActionEl : function(){
21948         return this[this.actionMode];
21949     },
21950
21951     initComponent : Roo.emptyFn,
21952     /**
21953      * If this is a lazy rendering component, render it to its container element.
21954      * @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.
21955      */
21956     render : function(container, position){
21957         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21958             if(!container && this.el){
21959                 this.el = Roo.get(this.el);
21960                 container = this.el.dom.parentNode;
21961                 this.allowDomMove = false;
21962             }
21963             this.container = Roo.get(container);
21964             this.rendered = true;
21965             if(position !== undefined){
21966                 if(typeof position == 'number'){
21967                     position = this.container.dom.childNodes[position];
21968                 }else{
21969                     position = Roo.getDom(position);
21970                 }
21971             }
21972             this.onRender(this.container, position || null);
21973             if(this.cls){
21974                 this.el.addClass(this.cls);
21975                 delete this.cls;
21976             }
21977             if(this.style){
21978                 this.el.applyStyles(this.style);
21979                 delete this.style;
21980             }
21981             this.fireEvent("render", this);
21982             this.afterRender(this.container);
21983             if(this.hidden){
21984                 this.hide();
21985             }
21986             if(this.disabled){
21987                 this.disable();
21988             }
21989         }
21990         return this;
21991     },
21992
21993     /** @private */
21994     // default function is not really useful
21995     onRender : function(ct, position){
21996         if(this.el){
21997             this.el = Roo.get(this.el);
21998             if(this.allowDomMove !== false){
21999                 ct.dom.insertBefore(this.el.dom, position);
22000             }
22001         }
22002     },
22003
22004     /** @private */
22005     getAutoCreate : function(){
22006         var cfg = typeof this.autoCreate == "object" ?
22007                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22008         if(this.id && !cfg.id){
22009             cfg.id = this.id;
22010         }
22011         return cfg;
22012     },
22013
22014     /** @private */
22015     afterRender : Roo.emptyFn,
22016
22017     /**
22018      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22019      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22020      */
22021     destroy : function(){
22022         if(this.fireEvent("beforedestroy", this) !== false){
22023             this.purgeListeners();
22024             this.beforeDestroy();
22025             if(this.rendered){
22026                 this.el.removeAllListeners();
22027                 this.el.remove();
22028                 if(this.actionMode == "container"){
22029                     this.container.remove();
22030                 }
22031             }
22032             this.onDestroy();
22033             Roo.ComponentMgr.unregister(this);
22034             this.fireEvent("destroy", this);
22035         }
22036     },
22037
22038         /** @private */
22039     beforeDestroy : function(){
22040
22041     },
22042
22043         /** @private */
22044         onDestroy : function(){
22045
22046     },
22047
22048     /**
22049      * Returns the underlying {@link Roo.Element}.
22050      * @return {Roo.Element} The element
22051      */
22052     getEl : function(){
22053         return this.el;
22054     },
22055
22056     /**
22057      * Returns the id of this component.
22058      * @return {String}
22059      */
22060     getId : function(){
22061         return this.id;
22062     },
22063
22064     /**
22065      * Try to focus this component.
22066      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22067      * @return {Roo.Component} this
22068      */
22069     focus : function(selectText){
22070         if(this.rendered){
22071             this.el.focus();
22072             if(selectText === true){
22073                 this.el.dom.select();
22074             }
22075         }
22076         return this;
22077     },
22078
22079     /** @private */
22080     blur : function(){
22081         if(this.rendered){
22082             this.el.blur();
22083         }
22084         return this;
22085     },
22086
22087     /**
22088      * Disable this component.
22089      * @return {Roo.Component} this
22090      */
22091     disable : function(){
22092         if(this.rendered){
22093             this.onDisable();
22094         }
22095         this.disabled = true;
22096         this.fireEvent("disable", this);
22097         return this;
22098     },
22099
22100         // private
22101     onDisable : function(){
22102         this.getActionEl().addClass(this.disabledClass);
22103         this.el.dom.disabled = true;
22104     },
22105
22106     /**
22107      * Enable this component.
22108      * @return {Roo.Component} this
22109      */
22110     enable : function(){
22111         if(this.rendered){
22112             this.onEnable();
22113         }
22114         this.disabled = false;
22115         this.fireEvent("enable", this);
22116         return this;
22117     },
22118
22119         // private
22120     onEnable : function(){
22121         this.getActionEl().removeClass(this.disabledClass);
22122         this.el.dom.disabled = false;
22123     },
22124
22125     /**
22126      * Convenience function for setting disabled/enabled by boolean.
22127      * @param {Boolean} disabled
22128      */
22129     setDisabled : function(disabled){
22130         this[disabled ? "disable" : "enable"]();
22131     },
22132
22133     /**
22134      * Show this component.
22135      * @return {Roo.Component} this
22136      */
22137     show: function(){
22138         if(this.fireEvent("beforeshow", this) !== false){
22139             this.hidden = false;
22140             if(this.rendered){
22141                 this.onShow();
22142             }
22143             this.fireEvent("show", this);
22144         }
22145         return this;
22146     },
22147
22148     // private
22149     onShow : function(){
22150         var ae = this.getActionEl();
22151         if(this.hideMode == 'visibility'){
22152             ae.dom.style.visibility = "visible";
22153         }else if(this.hideMode == 'offsets'){
22154             ae.removeClass('x-hidden');
22155         }else{
22156             ae.dom.style.display = "";
22157         }
22158     },
22159
22160     /**
22161      * Hide this component.
22162      * @return {Roo.Component} this
22163      */
22164     hide: function(){
22165         if(this.fireEvent("beforehide", this) !== false){
22166             this.hidden = true;
22167             if(this.rendered){
22168                 this.onHide();
22169             }
22170             this.fireEvent("hide", this);
22171         }
22172         return this;
22173     },
22174
22175     // private
22176     onHide : function(){
22177         var ae = this.getActionEl();
22178         if(this.hideMode == 'visibility'){
22179             ae.dom.style.visibility = "hidden";
22180         }else if(this.hideMode == 'offsets'){
22181             ae.addClass('x-hidden');
22182         }else{
22183             ae.dom.style.display = "none";
22184         }
22185     },
22186
22187     /**
22188      * Convenience function to hide or show this component by boolean.
22189      * @param {Boolean} visible True to show, false to hide
22190      * @return {Roo.Component} this
22191      */
22192     setVisible: function(visible){
22193         if(visible) {
22194             this.show();
22195         }else{
22196             this.hide();
22197         }
22198         return this;
22199     },
22200
22201     /**
22202      * Returns true if this component is visible.
22203      */
22204     isVisible : function(){
22205         return this.getActionEl().isVisible();
22206     },
22207
22208     cloneConfig : function(overrides){
22209         overrides = overrides || {};
22210         var id = overrides.id || Roo.id();
22211         var cfg = Roo.applyIf(overrides, this.initialConfig);
22212         cfg.id = id; // prevent dup id
22213         return new this.constructor(cfg);
22214     }
22215 });/*
22216  * Based on:
22217  * Ext JS Library 1.1.1
22218  * Copyright(c) 2006-2007, Ext JS, LLC.
22219  *
22220  * Originally Released Under LGPL - original licence link has changed is not relivant.
22221  *
22222  * Fork - LGPL
22223  * <script type="text/javascript">
22224  */
22225  (function(){ 
22226 /**
22227  * @class Roo.Layer
22228  * @extends Roo.Element
22229  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22230  * automatic maintaining of shadow/shim positions.
22231  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22232  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22233  * you can pass a string with a CSS class name. False turns off the shadow.
22234  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22235  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22236  * @cfg {String} cls CSS class to add to the element
22237  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22238  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22239  * @constructor
22240  * @param {Object} config An object with config options.
22241  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22242  */
22243
22244 Roo.Layer = function(config, existingEl){
22245     config = config || {};
22246     var dh = Roo.DomHelper;
22247     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22248     if(existingEl){
22249         this.dom = Roo.getDom(existingEl);
22250     }
22251     if(!this.dom){
22252         var o = config.dh || {tag: "div", cls: "x-layer"};
22253         this.dom = dh.append(pel, o);
22254     }
22255     if(config.cls){
22256         this.addClass(config.cls);
22257     }
22258     this.constrain = config.constrain !== false;
22259     this.visibilityMode = Roo.Element.VISIBILITY;
22260     if(config.id){
22261         this.id = this.dom.id = config.id;
22262     }else{
22263         this.id = Roo.id(this.dom);
22264     }
22265     this.zindex = config.zindex || this.getZIndex();
22266     this.position("absolute", this.zindex);
22267     if(config.shadow){
22268         this.shadowOffset = config.shadowOffset || 4;
22269         this.shadow = new Roo.Shadow({
22270             offset : this.shadowOffset,
22271             mode : config.shadow
22272         });
22273     }else{
22274         this.shadowOffset = 0;
22275     }
22276     this.useShim = config.shim !== false && Roo.useShims;
22277     this.useDisplay = config.useDisplay;
22278     this.hide();
22279 };
22280
22281 var supr = Roo.Element.prototype;
22282
22283 // shims are shared among layer to keep from having 100 iframes
22284 var shims = [];
22285
22286 Roo.extend(Roo.Layer, Roo.Element, {
22287
22288     getZIndex : function(){
22289         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22290     },
22291
22292     getShim : function(){
22293         if(!this.useShim){
22294             return null;
22295         }
22296         if(this.shim){
22297             return this.shim;
22298         }
22299         var shim = shims.shift();
22300         if(!shim){
22301             shim = this.createShim();
22302             shim.enableDisplayMode('block');
22303             shim.dom.style.display = 'none';
22304             shim.dom.style.visibility = 'visible';
22305         }
22306         var pn = this.dom.parentNode;
22307         if(shim.dom.parentNode != pn){
22308             pn.insertBefore(shim.dom, this.dom);
22309         }
22310         shim.setStyle('z-index', this.getZIndex()-2);
22311         this.shim = shim;
22312         return shim;
22313     },
22314
22315     hideShim : function(){
22316         if(this.shim){
22317             this.shim.setDisplayed(false);
22318             shims.push(this.shim);
22319             delete this.shim;
22320         }
22321     },
22322
22323     disableShadow : function(){
22324         if(this.shadow){
22325             this.shadowDisabled = true;
22326             this.shadow.hide();
22327             this.lastShadowOffset = this.shadowOffset;
22328             this.shadowOffset = 0;
22329         }
22330     },
22331
22332     enableShadow : function(show){
22333         if(this.shadow){
22334             this.shadowDisabled = false;
22335             this.shadowOffset = this.lastShadowOffset;
22336             delete this.lastShadowOffset;
22337             if(show){
22338                 this.sync(true);
22339             }
22340         }
22341     },
22342
22343     // private
22344     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22345     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22346     sync : function(doShow){
22347         var sw = this.shadow;
22348         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22349             var sh = this.getShim();
22350
22351             var w = this.getWidth(),
22352                 h = this.getHeight();
22353
22354             var l = this.getLeft(true),
22355                 t = this.getTop(true);
22356
22357             if(sw && !this.shadowDisabled){
22358                 if(doShow && !sw.isVisible()){
22359                     sw.show(this);
22360                 }else{
22361                     sw.realign(l, t, w, h);
22362                 }
22363                 if(sh){
22364                     if(doShow){
22365                        sh.show();
22366                     }
22367                     // fit the shim behind the shadow, so it is shimmed too
22368                     var a = sw.adjusts, s = sh.dom.style;
22369                     s.left = (Math.min(l, l+a.l))+"px";
22370                     s.top = (Math.min(t, t+a.t))+"px";
22371                     s.width = (w+a.w)+"px";
22372                     s.height = (h+a.h)+"px";
22373                 }
22374             }else if(sh){
22375                 if(doShow){
22376                    sh.show();
22377                 }
22378                 sh.setSize(w, h);
22379                 sh.setLeftTop(l, t);
22380             }
22381             
22382         }
22383     },
22384
22385     // private
22386     destroy : function(){
22387         this.hideShim();
22388         if(this.shadow){
22389             this.shadow.hide();
22390         }
22391         this.removeAllListeners();
22392         var pn = this.dom.parentNode;
22393         if(pn){
22394             pn.removeChild(this.dom);
22395         }
22396         Roo.Element.uncache(this.id);
22397     },
22398
22399     remove : function(){
22400         this.destroy();
22401     },
22402
22403     // private
22404     beginUpdate : function(){
22405         this.updating = true;
22406     },
22407
22408     // private
22409     endUpdate : function(){
22410         this.updating = false;
22411         this.sync(true);
22412     },
22413
22414     // private
22415     hideUnders : function(negOffset){
22416         if(this.shadow){
22417             this.shadow.hide();
22418         }
22419         this.hideShim();
22420     },
22421
22422     // private
22423     constrainXY : function(){
22424         if(this.constrain){
22425             var vw = Roo.lib.Dom.getViewWidth(),
22426                 vh = Roo.lib.Dom.getViewHeight();
22427             var s = Roo.get(document).getScroll();
22428
22429             var xy = this.getXY();
22430             var x = xy[0], y = xy[1];   
22431             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22432             // only move it if it needs it
22433             var moved = false;
22434             // first validate right/bottom
22435             if((x + w) > vw+s.left){
22436                 x = vw - w - this.shadowOffset;
22437                 moved = true;
22438             }
22439             if((y + h) > vh+s.top){
22440                 y = vh - h - this.shadowOffset;
22441                 moved = true;
22442             }
22443             // then make sure top/left isn't negative
22444             if(x < s.left){
22445                 x = s.left;
22446                 moved = true;
22447             }
22448             if(y < s.top){
22449                 y = s.top;
22450                 moved = true;
22451             }
22452             if(moved){
22453                 if(this.avoidY){
22454                     var ay = this.avoidY;
22455                     if(y <= ay && (y+h) >= ay){
22456                         y = ay-h-5;   
22457                     }
22458                 }
22459                 xy = [x, y];
22460                 this.storeXY(xy);
22461                 supr.setXY.call(this, xy);
22462                 this.sync();
22463             }
22464         }
22465     },
22466
22467     isVisible : function(){
22468         return this.visible;    
22469     },
22470
22471     // private
22472     showAction : function(){
22473         this.visible = true; // track visibility to prevent getStyle calls
22474         if(this.useDisplay === true){
22475             this.setDisplayed("");
22476         }else if(this.lastXY){
22477             supr.setXY.call(this, this.lastXY);
22478         }else if(this.lastLT){
22479             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22480         }
22481     },
22482
22483     // private
22484     hideAction : function(){
22485         this.visible = false;
22486         if(this.useDisplay === true){
22487             this.setDisplayed(false);
22488         }else{
22489             this.setLeftTop(-10000,-10000);
22490         }
22491     },
22492
22493     // overridden Element method
22494     setVisible : function(v, a, d, c, e){
22495         if(v){
22496             this.showAction();
22497         }
22498         if(a && v){
22499             var cb = function(){
22500                 this.sync(true);
22501                 if(c){
22502                     c();
22503                 }
22504             }.createDelegate(this);
22505             supr.setVisible.call(this, true, true, d, cb, e);
22506         }else{
22507             if(!v){
22508                 this.hideUnders(true);
22509             }
22510             var cb = c;
22511             if(a){
22512                 cb = function(){
22513                     this.hideAction();
22514                     if(c){
22515                         c();
22516                     }
22517                 }.createDelegate(this);
22518             }
22519             supr.setVisible.call(this, v, a, d, cb, e);
22520             if(v){
22521                 this.sync(true);
22522             }else if(!a){
22523                 this.hideAction();
22524             }
22525         }
22526     },
22527
22528     storeXY : function(xy){
22529         delete this.lastLT;
22530         this.lastXY = xy;
22531     },
22532
22533     storeLeftTop : function(left, top){
22534         delete this.lastXY;
22535         this.lastLT = [left, top];
22536     },
22537
22538     // private
22539     beforeFx : function(){
22540         this.beforeAction();
22541         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22542     },
22543
22544     // private
22545     afterFx : function(){
22546         Roo.Layer.superclass.afterFx.apply(this, arguments);
22547         this.sync(this.isVisible());
22548     },
22549
22550     // private
22551     beforeAction : function(){
22552         if(!this.updating && this.shadow){
22553             this.shadow.hide();
22554         }
22555     },
22556
22557     // overridden Element method
22558     setLeft : function(left){
22559         this.storeLeftTop(left, this.getTop(true));
22560         supr.setLeft.apply(this, arguments);
22561         this.sync();
22562     },
22563
22564     setTop : function(top){
22565         this.storeLeftTop(this.getLeft(true), top);
22566         supr.setTop.apply(this, arguments);
22567         this.sync();
22568     },
22569
22570     setLeftTop : function(left, top){
22571         this.storeLeftTop(left, top);
22572         supr.setLeftTop.apply(this, arguments);
22573         this.sync();
22574     },
22575
22576     setXY : function(xy, a, d, c, e){
22577         this.fixDisplay();
22578         this.beforeAction();
22579         this.storeXY(xy);
22580         var cb = this.createCB(c);
22581         supr.setXY.call(this, xy, a, d, cb, e);
22582         if(!a){
22583             cb();
22584         }
22585     },
22586
22587     // private
22588     createCB : function(c){
22589         var el = this;
22590         return function(){
22591             el.constrainXY();
22592             el.sync(true);
22593             if(c){
22594                 c();
22595             }
22596         };
22597     },
22598
22599     // overridden Element method
22600     setX : function(x, a, d, c, e){
22601         this.setXY([x, this.getY()], a, d, c, e);
22602     },
22603
22604     // overridden Element method
22605     setY : function(y, a, d, c, e){
22606         this.setXY([this.getX(), y], a, d, c, e);
22607     },
22608
22609     // overridden Element method
22610     setSize : function(w, h, a, d, c, e){
22611         this.beforeAction();
22612         var cb = this.createCB(c);
22613         supr.setSize.call(this, w, h, a, d, cb, e);
22614         if(!a){
22615             cb();
22616         }
22617     },
22618
22619     // overridden Element method
22620     setWidth : function(w, a, d, c, e){
22621         this.beforeAction();
22622         var cb = this.createCB(c);
22623         supr.setWidth.call(this, w, a, d, cb, e);
22624         if(!a){
22625             cb();
22626         }
22627     },
22628
22629     // overridden Element method
22630     setHeight : function(h, a, d, c, e){
22631         this.beforeAction();
22632         var cb = this.createCB(c);
22633         supr.setHeight.call(this, h, a, d, cb, e);
22634         if(!a){
22635             cb();
22636         }
22637     },
22638
22639     // overridden Element method
22640     setBounds : function(x, y, w, h, a, d, c, e){
22641         this.beforeAction();
22642         var cb = this.createCB(c);
22643         if(!a){
22644             this.storeXY([x, y]);
22645             supr.setXY.call(this, [x, y]);
22646             supr.setSize.call(this, w, h, a, d, cb, e);
22647             cb();
22648         }else{
22649             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22650         }
22651         return this;
22652     },
22653     
22654     /**
22655      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22656      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22657      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22658      * @param {Number} zindex The new z-index to set
22659      * @return {this} The Layer
22660      */
22661     setZIndex : function(zindex){
22662         this.zindex = zindex;
22663         this.setStyle("z-index", zindex + 2);
22664         if(this.shadow){
22665             this.shadow.setZIndex(zindex + 1);
22666         }
22667         if(this.shim){
22668             this.shim.setStyle("z-index", zindex);
22669         }
22670     }
22671 });
22672 })();/*
22673  * Based on:
22674  * Ext JS Library 1.1.1
22675  * Copyright(c) 2006-2007, Ext JS, LLC.
22676  *
22677  * Originally Released Under LGPL - original licence link has changed is not relivant.
22678  *
22679  * Fork - LGPL
22680  * <script type="text/javascript">
22681  */
22682
22683
22684 /**
22685  * @class Roo.Shadow
22686  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22687  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22688  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22689  * @constructor
22690  * Create a new Shadow
22691  * @param {Object} config The config object
22692  */
22693 Roo.Shadow = function(config){
22694     Roo.apply(this, config);
22695     if(typeof this.mode != "string"){
22696         this.mode = this.defaultMode;
22697     }
22698     var o = this.offset, a = {h: 0};
22699     var rad = Math.floor(this.offset/2);
22700     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22701         case "drop":
22702             a.w = 0;
22703             a.l = a.t = o;
22704             a.t -= 1;
22705             if(Roo.isIE){
22706                 a.l -= this.offset + rad;
22707                 a.t -= this.offset + rad;
22708                 a.w -= rad;
22709                 a.h -= rad;
22710                 a.t += 1;
22711             }
22712         break;
22713         case "sides":
22714             a.w = (o*2);
22715             a.l = -o;
22716             a.t = o-1;
22717             if(Roo.isIE){
22718                 a.l -= (this.offset - rad);
22719                 a.t -= this.offset + rad;
22720                 a.l += 1;
22721                 a.w -= (this.offset - rad)*2;
22722                 a.w -= rad + 1;
22723                 a.h -= 1;
22724             }
22725         break;
22726         case "frame":
22727             a.w = a.h = (o*2);
22728             a.l = a.t = -o;
22729             a.t += 1;
22730             a.h -= 2;
22731             if(Roo.isIE){
22732                 a.l -= (this.offset - rad);
22733                 a.t -= (this.offset - rad);
22734                 a.l += 1;
22735                 a.w -= (this.offset + rad + 1);
22736                 a.h -= (this.offset + rad);
22737                 a.h += 1;
22738             }
22739         break;
22740     };
22741
22742     this.adjusts = a;
22743 };
22744
22745 Roo.Shadow.prototype = {
22746     /**
22747      * @cfg {String} mode
22748      * The shadow display mode.  Supports the following options:<br />
22749      * sides: Shadow displays on both sides and bottom only<br />
22750      * frame: Shadow displays equally on all four sides<br />
22751      * drop: Traditional bottom-right drop shadow (default)
22752      */
22753     /**
22754      * @cfg {String} offset
22755      * The number of pixels to offset the shadow from the element (defaults to 4)
22756      */
22757     offset: 4,
22758
22759     // private
22760     defaultMode: "drop",
22761
22762     /**
22763      * Displays the shadow under the target element
22764      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22765      */
22766     show : function(target){
22767         target = Roo.get(target);
22768         if(!this.el){
22769             this.el = Roo.Shadow.Pool.pull();
22770             if(this.el.dom.nextSibling != target.dom){
22771                 this.el.insertBefore(target);
22772             }
22773         }
22774         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22775         if(Roo.isIE){
22776             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22777         }
22778         this.realign(
22779             target.getLeft(true),
22780             target.getTop(true),
22781             target.getWidth(),
22782             target.getHeight()
22783         );
22784         this.el.dom.style.display = "block";
22785     },
22786
22787     /**
22788      * Returns true if the shadow is visible, else false
22789      */
22790     isVisible : function(){
22791         return this.el ? true : false;  
22792     },
22793
22794     /**
22795      * Direct alignment when values are already available. Show must be called at least once before
22796      * calling this method to ensure it is initialized.
22797      * @param {Number} left The target element left position
22798      * @param {Number} top The target element top position
22799      * @param {Number} width The target element width
22800      * @param {Number} height The target element height
22801      */
22802     realign : function(l, t, w, h){
22803         if(!this.el){
22804             return;
22805         }
22806         var a = this.adjusts, d = this.el.dom, s = d.style;
22807         var iea = 0;
22808         s.left = (l+a.l)+"px";
22809         s.top = (t+a.t)+"px";
22810         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22811  
22812         if(s.width != sws || s.height != shs){
22813             s.width = sws;
22814             s.height = shs;
22815             if(!Roo.isIE){
22816                 var cn = d.childNodes;
22817                 var sww = Math.max(0, (sw-12))+"px";
22818                 cn[0].childNodes[1].style.width = sww;
22819                 cn[1].childNodes[1].style.width = sww;
22820                 cn[2].childNodes[1].style.width = sww;
22821                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22822             }
22823         }
22824     },
22825
22826     /**
22827      * Hides this shadow
22828      */
22829     hide : function(){
22830         if(this.el){
22831             this.el.dom.style.display = "none";
22832             Roo.Shadow.Pool.push(this.el);
22833             delete this.el;
22834         }
22835     },
22836
22837     /**
22838      * Adjust the z-index of this shadow
22839      * @param {Number} zindex The new z-index
22840      */
22841     setZIndex : function(z){
22842         this.zIndex = z;
22843         if(this.el){
22844             this.el.setStyle("z-index", z);
22845         }
22846     }
22847 };
22848
22849 // Private utility class that manages the internal Shadow cache
22850 Roo.Shadow.Pool = function(){
22851     var p = [];
22852     var markup = Roo.isIE ?
22853                  '<div class="x-ie-shadow"></div>' :
22854                  '<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>';
22855     return {
22856         pull : function(){
22857             var sh = p.shift();
22858             if(!sh){
22859                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22860                 sh.autoBoxAdjust = false;
22861             }
22862             return sh;
22863         },
22864
22865         push : function(sh){
22866             p.push(sh);
22867         }
22868     };
22869 }();/*
22870  * Based on:
22871  * Ext JS Library 1.1.1
22872  * Copyright(c) 2006-2007, Ext JS, LLC.
22873  *
22874  * Originally Released Under LGPL - original licence link has changed is not relivant.
22875  *
22876  * Fork - LGPL
22877  * <script type="text/javascript">
22878  */
22879
22880 /**
22881  * @class Roo.BoxComponent
22882  * @extends Roo.Component
22883  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22884  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22885  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22886  * layout containers.
22887  * @constructor
22888  * @param {Roo.Element/String/Object} config The configuration options.
22889  */
22890 Roo.BoxComponent = function(config){
22891     Roo.Component.call(this, config);
22892     this.addEvents({
22893         /**
22894          * @event resize
22895          * Fires after the component is resized.
22896              * @param {Roo.Component} this
22897              * @param {Number} adjWidth The box-adjusted width that was set
22898              * @param {Number} adjHeight The box-adjusted height that was set
22899              * @param {Number} rawWidth The width that was originally specified
22900              * @param {Number} rawHeight The height that was originally specified
22901              */
22902         resize : true,
22903         /**
22904          * @event move
22905          * Fires after the component is moved.
22906              * @param {Roo.Component} this
22907              * @param {Number} x The new x position
22908              * @param {Number} y The new y position
22909              */
22910         move : true
22911     });
22912 };
22913
22914 Roo.extend(Roo.BoxComponent, Roo.Component, {
22915     // private, set in afterRender to signify that the component has been rendered
22916     boxReady : false,
22917     // private, used to defer height settings to subclasses
22918     deferHeight: false,
22919     /** @cfg {Number} width
22920      * width (optional) size of component
22921      */
22922      /** @cfg {Number} height
22923      * height (optional) size of component
22924      */
22925      
22926     /**
22927      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22928      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22929      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22930      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22931      * @return {Roo.BoxComponent} this
22932      */
22933     setSize : function(w, h){
22934         // support for standard size objects
22935         if(typeof w == 'object'){
22936             h = w.height;
22937             w = w.width;
22938         }
22939         // not rendered
22940         if(!this.boxReady){
22941             this.width = w;
22942             this.height = h;
22943             return this;
22944         }
22945
22946         // prevent recalcs when not needed
22947         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22948             return this;
22949         }
22950         this.lastSize = {width: w, height: h};
22951
22952         var adj = this.adjustSize(w, h);
22953         var aw = adj.width, ah = adj.height;
22954         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22955             var rz = this.getResizeEl();
22956             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22957                 rz.setSize(aw, ah);
22958             }else if(!this.deferHeight && ah !== undefined){
22959                 rz.setHeight(ah);
22960             }else if(aw !== undefined){
22961                 rz.setWidth(aw);
22962             }
22963             this.onResize(aw, ah, w, h);
22964             this.fireEvent('resize', this, aw, ah, w, h);
22965         }
22966         return this;
22967     },
22968
22969     /**
22970      * Gets the current size of the component's underlying element.
22971      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22972      */
22973     getSize : function(){
22974         return this.el.getSize();
22975     },
22976
22977     /**
22978      * Gets the current XY position of the component's underlying element.
22979      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22980      * @return {Array} The XY position of the element (e.g., [100, 200])
22981      */
22982     getPosition : function(local){
22983         if(local === true){
22984             return [this.el.getLeft(true), this.el.getTop(true)];
22985         }
22986         return this.xy || this.el.getXY();
22987     },
22988
22989     /**
22990      * Gets the current box measurements of the component's underlying element.
22991      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22992      * @returns {Object} box An object in the format {x, y, width, height}
22993      */
22994     getBox : function(local){
22995         var s = this.el.getSize();
22996         if(local){
22997             s.x = this.el.getLeft(true);
22998             s.y = this.el.getTop(true);
22999         }else{
23000             var xy = this.xy || this.el.getXY();
23001             s.x = xy[0];
23002             s.y = xy[1];
23003         }
23004         return s;
23005     },
23006
23007     /**
23008      * Sets the current box measurements of the component's underlying element.
23009      * @param {Object} box An object in the format {x, y, width, height}
23010      * @returns {Roo.BoxComponent} this
23011      */
23012     updateBox : function(box){
23013         this.setSize(box.width, box.height);
23014         this.setPagePosition(box.x, box.y);
23015         return this;
23016     },
23017
23018     // protected
23019     getResizeEl : function(){
23020         return this.resizeEl || this.el;
23021     },
23022
23023     // protected
23024     getPositionEl : function(){
23025         return this.positionEl || this.el;
23026     },
23027
23028     /**
23029      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23030      * This method fires the move event.
23031      * @param {Number} left The new left
23032      * @param {Number} top The new top
23033      * @returns {Roo.BoxComponent} this
23034      */
23035     setPosition : function(x, y){
23036         this.x = x;
23037         this.y = y;
23038         if(!this.boxReady){
23039             return this;
23040         }
23041         var adj = this.adjustPosition(x, y);
23042         var ax = adj.x, ay = adj.y;
23043
23044         var el = this.getPositionEl();
23045         if(ax !== undefined || ay !== undefined){
23046             if(ax !== undefined && ay !== undefined){
23047                 el.setLeftTop(ax, ay);
23048             }else if(ax !== undefined){
23049                 el.setLeft(ax);
23050             }else if(ay !== undefined){
23051                 el.setTop(ay);
23052             }
23053             this.onPosition(ax, ay);
23054             this.fireEvent('move', this, ax, ay);
23055         }
23056         return this;
23057     },
23058
23059     /**
23060      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23061      * This method fires the move event.
23062      * @param {Number} x The new x position
23063      * @param {Number} y The new y position
23064      * @returns {Roo.BoxComponent} this
23065      */
23066     setPagePosition : function(x, y){
23067         this.pageX = x;
23068         this.pageY = y;
23069         if(!this.boxReady){
23070             return;
23071         }
23072         if(x === undefined || y === undefined){ // cannot translate undefined points
23073             return;
23074         }
23075         var p = this.el.translatePoints(x, y);
23076         this.setPosition(p.left, p.top);
23077         return this;
23078     },
23079
23080     // private
23081     onRender : function(ct, position){
23082         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23083         if(this.resizeEl){
23084             this.resizeEl = Roo.get(this.resizeEl);
23085         }
23086         if(this.positionEl){
23087             this.positionEl = Roo.get(this.positionEl);
23088         }
23089     },
23090
23091     // private
23092     afterRender : function(){
23093         Roo.BoxComponent.superclass.afterRender.call(this);
23094         this.boxReady = true;
23095         this.setSize(this.width, this.height);
23096         if(this.x || this.y){
23097             this.setPosition(this.x, this.y);
23098         }
23099         if(this.pageX || this.pageY){
23100             this.setPagePosition(this.pageX, this.pageY);
23101         }
23102     },
23103
23104     /**
23105      * Force the component's size to recalculate based on the underlying element's current height and width.
23106      * @returns {Roo.BoxComponent} this
23107      */
23108     syncSize : function(){
23109         delete this.lastSize;
23110         this.setSize(this.el.getWidth(), this.el.getHeight());
23111         return this;
23112     },
23113
23114     /**
23115      * Called after the component is resized, this method is empty by default but can be implemented by any
23116      * subclass that needs to perform custom logic after a resize occurs.
23117      * @param {Number} adjWidth The box-adjusted width that was set
23118      * @param {Number} adjHeight The box-adjusted height that was set
23119      * @param {Number} rawWidth The width that was originally specified
23120      * @param {Number} rawHeight The height that was originally specified
23121      */
23122     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23123
23124     },
23125
23126     /**
23127      * Called after the component is moved, this method is empty by default but can be implemented by any
23128      * subclass that needs to perform custom logic after a move occurs.
23129      * @param {Number} x The new x position
23130      * @param {Number} y The new y position
23131      */
23132     onPosition : function(x, y){
23133
23134     },
23135
23136     // private
23137     adjustSize : function(w, h){
23138         if(this.autoWidth){
23139             w = 'auto';
23140         }
23141         if(this.autoHeight){
23142             h = 'auto';
23143         }
23144         return {width : w, height: h};
23145     },
23146
23147     // private
23148     adjustPosition : function(x, y){
23149         return {x : x, y: y};
23150     }
23151 });/*
23152  * Based on:
23153  * Ext JS Library 1.1.1
23154  * Copyright(c) 2006-2007, Ext JS, LLC.
23155  *
23156  * Originally Released Under LGPL - original licence link has changed is not relivant.
23157  *
23158  * Fork - LGPL
23159  * <script type="text/javascript">
23160  */
23161
23162
23163 /**
23164  * @class Roo.SplitBar
23165  * @extends Roo.util.Observable
23166  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23167  * <br><br>
23168  * Usage:
23169  * <pre><code>
23170 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23171                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23172 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23173 split.minSize = 100;
23174 split.maxSize = 600;
23175 split.animate = true;
23176 split.on('moved', splitterMoved);
23177 </code></pre>
23178  * @constructor
23179  * Create a new SplitBar
23180  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23181  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23182  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23183  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23184                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23185                         position of the SplitBar).
23186  */
23187 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23188     
23189     /** @private */
23190     this.el = Roo.get(dragElement, true);
23191     this.el.dom.unselectable = "on";
23192     /** @private */
23193     this.resizingEl = Roo.get(resizingElement, true);
23194
23195     /**
23196      * @private
23197      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23198      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23199      * @type Number
23200      */
23201     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23202     
23203     /**
23204      * The minimum size of the resizing element. (Defaults to 0)
23205      * @type Number
23206      */
23207     this.minSize = 0;
23208     
23209     /**
23210      * The maximum size of the resizing element. (Defaults to 2000)
23211      * @type Number
23212      */
23213     this.maxSize = 2000;
23214     
23215     /**
23216      * Whether to animate the transition to the new size
23217      * @type Boolean
23218      */
23219     this.animate = false;
23220     
23221     /**
23222      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23223      * @type Boolean
23224      */
23225     this.useShim = false;
23226     
23227     /** @private */
23228     this.shim = null;
23229     
23230     if(!existingProxy){
23231         /** @private */
23232         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23233     }else{
23234         this.proxy = Roo.get(existingProxy).dom;
23235     }
23236     /** @private */
23237     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23238     
23239     /** @private */
23240     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23241     
23242     /** @private */
23243     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23244     
23245     /** @private */
23246     this.dragSpecs = {};
23247     
23248     /**
23249      * @private The adapter to use to positon and resize elements
23250      */
23251     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23252     this.adapter.init(this);
23253     
23254     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23255         /** @private */
23256         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23257         this.el.addClass("x-splitbar-h");
23258     }else{
23259         /** @private */
23260         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23261         this.el.addClass("x-splitbar-v");
23262     }
23263     
23264     this.addEvents({
23265         /**
23266          * @event resize
23267          * Fires when the splitter is moved (alias for {@link #event-moved})
23268          * @param {Roo.SplitBar} this
23269          * @param {Number} newSize the new width or height
23270          */
23271         "resize" : true,
23272         /**
23273          * @event moved
23274          * Fires when the splitter is moved
23275          * @param {Roo.SplitBar} this
23276          * @param {Number} newSize the new width or height
23277          */
23278         "moved" : true,
23279         /**
23280          * @event beforeresize
23281          * Fires before the splitter is dragged
23282          * @param {Roo.SplitBar} this
23283          */
23284         "beforeresize" : true,
23285
23286         "beforeapply" : true
23287     });
23288
23289     Roo.util.Observable.call(this);
23290 };
23291
23292 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23293     onStartProxyDrag : function(x, y){
23294         this.fireEvent("beforeresize", this);
23295         if(!this.overlay){
23296             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23297             o.unselectable();
23298             o.enableDisplayMode("block");
23299             // all splitbars share the same overlay
23300             Roo.SplitBar.prototype.overlay = o;
23301         }
23302         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23303         this.overlay.show();
23304         Roo.get(this.proxy).setDisplayed("block");
23305         var size = this.adapter.getElementSize(this);
23306         this.activeMinSize = this.getMinimumSize();;
23307         this.activeMaxSize = this.getMaximumSize();;
23308         var c1 = size - this.activeMinSize;
23309         var c2 = Math.max(this.activeMaxSize - size, 0);
23310         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23311             this.dd.resetConstraints();
23312             this.dd.setXConstraint(
23313                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23314                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23315             );
23316             this.dd.setYConstraint(0, 0);
23317         }else{
23318             this.dd.resetConstraints();
23319             this.dd.setXConstraint(0, 0);
23320             this.dd.setYConstraint(
23321                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23322                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23323             );
23324          }
23325         this.dragSpecs.startSize = size;
23326         this.dragSpecs.startPoint = [x, y];
23327         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23328     },
23329     
23330     /** 
23331      * @private Called after the drag operation by the DDProxy
23332      */
23333     onEndProxyDrag : function(e){
23334         Roo.get(this.proxy).setDisplayed(false);
23335         var endPoint = Roo.lib.Event.getXY(e);
23336         if(this.overlay){
23337             this.overlay.hide();
23338         }
23339         var newSize;
23340         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23341             newSize = this.dragSpecs.startSize + 
23342                 (this.placement == Roo.SplitBar.LEFT ?
23343                     endPoint[0] - this.dragSpecs.startPoint[0] :
23344                     this.dragSpecs.startPoint[0] - endPoint[0]
23345                 );
23346         }else{
23347             newSize = this.dragSpecs.startSize + 
23348                 (this.placement == Roo.SplitBar.TOP ?
23349                     endPoint[1] - this.dragSpecs.startPoint[1] :
23350                     this.dragSpecs.startPoint[1] - endPoint[1]
23351                 );
23352         }
23353         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23354         if(newSize != this.dragSpecs.startSize){
23355             if(this.fireEvent('beforeapply', this, newSize) !== false){
23356                 this.adapter.setElementSize(this, newSize);
23357                 this.fireEvent("moved", this, newSize);
23358                 this.fireEvent("resize", this, newSize);
23359             }
23360         }
23361     },
23362     
23363     /**
23364      * Get the adapter this SplitBar uses
23365      * @return The adapter object
23366      */
23367     getAdapter : function(){
23368         return this.adapter;
23369     },
23370     
23371     /**
23372      * Set the adapter this SplitBar uses
23373      * @param {Object} adapter A SplitBar adapter object
23374      */
23375     setAdapter : function(adapter){
23376         this.adapter = adapter;
23377         this.adapter.init(this);
23378     },
23379     
23380     /**
23381      * Gets the minimum size for the resizing element
23382      * @return {Number} The minimum size
23383      */
23384     getMinimumSize : function(){
23385         return this.minSize;
23386     },
23387     
23388     /**
23389      * Sets the minimum size for the resizing element
23390      * @param {Number} minSize The minimum size
23391      */
23392     setMinimumSize : function(minSize){
23393         this.minSize = minSize;
23394     },
23395     
23396     /**
23397      * Gets the maximum size for the resizing element
23398      * @return {Number} The maximum size
23399      */
23400     getMaximumSize : function(){
23401         return this.maxSize;
23402     },
23403     
23404     /**
23405      * Sets the maximum size for the resizing element
23406      * @param {Number} maxSize The maximum size
23407      */
23408     setMaximumSize : function(maxSize){
23409         this.maxSize = maxSize;
23410     },
23411     
23412     /**
23413      * Sets the initialize size for the resizing element
23414      * @param {Number} size The initial size
23415      */
23416     setCurrentSize : function(size){
23417         var oldAnimate = this.animate;
23418         this.animate = false;
23419         this.adapter.setElementSize(this, size);
23420         this.animate = oldAnimate;
23421     },
23422     
23423     /**
23424      * Destroy this splitbar. 
23425      * @param {Boolean} removeEl True to remove the element
23426      */
23427     destroy : function(removeEl){
23428         if(this.shim){
23429             this.shim.remove();
23430         }
23431         this.dd.unreg();
23432         this.proxy.parentNode.removeChild(this.proxy);
23433         if(removeEl){
23434             this.el.remove();
23435         }
23436     }
23437 });
23438
23439 /**
23440  * @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.
23441  */
23442 Roo.SplitBar.createProxy = function(dir){
23443     var proxy = new Roo.Element(document.createElement("div"));
23444     proxy.unselectable();
23445     var cls = 'x-splitbar-proxy';
23446     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23447     document.body.appendChild(proxy.dom);
23448     return proxy.dom;
23449 };
23450
23451 /** 
23452  * @class Roo.SplitBar.BasicLayoutAdapter
23453  * Default Adapter. It assumes the splitter and resizing element are not positioned
23454  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23455  */
23456 Roo.SplitBar.BasicLayoutAdapter = function(){
23457 };
23458
23459 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23460     // do nothing for now
23461     init : function(s){
23462     
23463     },
23464     /**
23465      * Called before drag operations to get the current size of the resizing element. 
23466      * @param {Roo.SplitBar} s The SplitBar using this adapter
23467      */
23468      getElementSize : function(s){
23469         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23470             return s.resizingEl.getWidth();
23471         }else{
23472             return s.resizingEl.getHeight();
23473         }
23474     },
23475     
23476     /**
23477      * Called after drag operations to set the size of the resizing element.
23478      * @param {Roo.SplitBar} s The SplitBar using this adapter
23479      * @param {Number} newSize The new size to set
23480      * @param {Function} onComplete A function to be invoked when resizing is complete
23481      */
23482     setElementSize : function(s, newSize, onComplete){
23483         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23484             if(!s.animate){
23485                 s.resizingEl.setWidth(newSize);
23486                 if(onComplete){
23487                     onComplete(s, newSize);
23488                 }
23489             }else{
23490                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23491             }
23492         }else{
23493             
23494             if(!s.animate){
23495                 s.resizingEl.setHeight(newSize);
23496                 if(onComplete){
23497                     onComplete(s, newSize);
23498                 }
23499             }else{
23500                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23501             }
23502         }
23503     }
23504 };
23505
23506 /** 
23507  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23508  * @extends Roo.SplitBar.BasicLayoutAdapter
23509  * Adapter that  moves the splitter element to align with the resized sizing element. 
23510  * Used with an absolute positioned SplitBar.
23511  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23512  * document.body, make sure you assign an id to the body element.
23513  */
23514 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23515     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23516     this.container = Roo.get(container);
23517 };
23518
23519 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23520     init : function(s){
23521         this.basic.init(s);
23522     },
23523     
23524     getElementSize : function(s){
23525         return this.basic.getElementSize(s);
23526     },
23527     
23528     setElementSize : function(s, newSize, onComplete){
23529         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23530     },
23531     
23532     moveSplitter : function(s){
23533         var yes = Roo.SplitBar;
23534         switch(s.placement){
23535             case yes.LEFT:
23536                 s.el.setX(s.resizingEl.getRight());
23537                 break;
23538             case yes.RIGHT:
23539                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23540                 break;
23541             case yes.TOP:
23542                 s.el.setY(s.resizingEl.getBottom());
23543                 break;
23544             case yes.BOTTOM:
23545                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23546                 break;
23547         }
23548     }
23549 };
23550
23551 /**
23552  * Orientation constant - Create a vertical SplitBar
23553  * @static
23554  * @type Number
23555  */
23556 Roo.SplitBar.VERTICAL = 1;
23557
23558 /**
23559  * Orientation constant - Create a horizontal SplitBar
23560  * @static
23561  * @type Number
23562  */
23563 Roo.SplitBar.HORIZONTAL = 2;
23564
23565 /**
23566  * Placement constant - The resizing element is to the left of the splitter element
23567  * @static
23568  * @type Number
23569  */
23570 Roo.SplitBar.LEFT = 1;
23571
23572 /**
23573  * Placement constant - The resizing element is to the right of the splitter element
23574  * @static
23575  * @type Number
23576  */
23577 Roo.SplitBar.RIGHT = 2;
23578
23579 /**
23580  * Placement constant - The resizing element is positioned above the splitter element
23581  * @static
23582  * @type Number
23583  */
23584 Roo.SplitBar.TOP = 3;
23585
23586 /**
23587  * Placement constant - The resizing element is positioned under splitter element
23588  * @static
23589  * @type Number
23590  */
23591 Roo.SplitBar.BOTTOM = 4;
23592 /*
23593  * Based on:
23594  * Ext JS Library 1.1.1
23595  * Copyright(c) 2006-2007, Ext JS, LLC.
23596  *
23597  * Originally Released Under LGPL - original licence link has changed is not relivant.
23598  *
23599  * Fork - LGPL
23600  * <script type="text/javascript">
23601  */
23602
23603 /**
23604  * @class Roo.View
23605  * @extends Roo.util.Observable
23606  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23607  * This class also supports single and multi selection modes. <br>
23608  * Create a data model bound view:
23609  <pre><code>
23610  var store = new Roo.data.Store(...);
23611
23612  var view = new Roo.View({
23613     el : "my-element",
23614     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23615  
23616     singleSelect: true,
23617     selectedClass: "ydataview-selected",
23618     store: store
23619  });
23620
23621  // listen for node click?
23622  view.on("click", function(vw, index, node, e){
23623  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23624  });
23625
23626  // load XML data
23627  dataModel.load("foobar.xml");
23628  </code></pre>
23629  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23630  * <br><br>
23631  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23632  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23633  * 
23634  * Note: old style constructor is still suported (container, template, config)
23635  * 
23636  * @constructor
23637  * Create a new View
23638  * @param {Object} config The config object
23639  * 
23640  */
23641 Roo.View = function(config, depreciated_tpl, depreciated_config){
23642     
23643     if (typeof(depreciated_tpl) == 'undefined') {
23644         // new way.. - universal constructor.
23645         Roo.apply(this, config);
23646         this.el  = Roo.get(this.el);
23647     } else {
23648         // old format..
23649         this.el  = Roo.get(config);
23650         this.tpl = depreciated_tpl;
23651         Roo.apply(this, depreciated_config);
23652     }
23653      
23654     
23655     if(typeof(this.tpl) == "string"){
23656         this.tpl = new Roo.Template(this.tpl);
23657     } else {
23658         // support xtype ctors..
23659         this.tpl = new Roo.factory(this.tpl, Roo);
23660     }
23661     
23662     
23663     this.tpl.compile();
23664    
23665
23666      
23667     /** @private */
23668     this.addEvents({
23669         /**
23670          * @event beforeclick
23671          * Fires before a click is processed. Returns false to cancel the default action.
23672          * @param {Roo.View} this
23673          * @param {Number} index The index of the target node
23674          * @param {HTMLElement} node The target node
23675          * @param {Roo.EventObject} e The raw event object
23676          */
23677             "beforeclick" : true,
23678         /**
23679          * @event click
23680          * Fires when a template node is clicked.
23681          * @param {Roo.View} this
23682          * @param {Number} index The index of the target node
23683          * @param {HTMLElement} node The target node
23684          * @param {Roo.EventObject} e The raw event object
23685          */
23686             "click" : true,
23687         /**
23688          * @event dblclick
23689          * Fires when a template node is double clicked.
23690          * @param {Roo.View} this
23691          * @param {Number} index The index of the target node
23692          * @param {HTMLElement} node The target node
23693          * @param {Roo.EventObject} e The raw event object
23694          */
23695             "dblclick" : true,
23696         /**
23697          * @event contextmenu
23698          * Fires when a template node is right clicked.
23699          * @param {Roo.View} this
23700          * @param {Number} index The index of the target node
23701          * @param {HTMLElement} node The target node
23702          * @param {Roo.EventObject} e The raw event object
23703          */
23704             "contextmenu" : true,
23705         /**
23706          * @event selectionchange
23707          * Fires when the selected nodes change.
23708          * @param {Roo.View} this
23709          * @param {Array} selections Array of the selected nodes
23710          */
23711             "selectionchange" : true,
23712     
23713         /**
23714          * @event beforeselect
23715          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23716          * @param {Roo.View} this
23717          * @param {HTMLElement} node The node to be selected
23718          * @param {Array} selections Array of currently selected nodes
23719          */
23720             "beforeselect" : true,
23721         /**
23722          * @event preparedata
23723          * Fires on every row to render, to allow you to change the data.
23724          * @param {Roo.View} this
23725          * @param {Object} data to be rendered (change this)
23726          */
23727           "preparedata" : true
23728         });
23729
23730     this.el.on({
23731         "click": this.onClick,
23732         "dblclick": this.onDblClick,
23733         "contextmenu": this.onContextMenu,
23734         scope:this
23735     });
23736
23737     this.selections = [];
23738     this.nodes = [];
23739     this.cmp = new Roo.CompositeElementLite([]);
23740     if(this.store){
23741         this.store = Roo.factory(this.store, Roo.data);
23742         this.setStore(this.store, true);
23743     }
23744     Roo.View.superclass.constructor.call(this);
23745 };
23746
23747 Roo.extend(Roo.View, Roo.util.Observable, {
23748     
23749      /**
23750      * @cfg {Roo.data.Store} store Data store to load data from.
23751      */
23752     store : false,
23753     
23754     /**
23755      * @cfg {String|Roo.Element} el The container element.
23756      */
23757     el : '',
23758     
23759     /**
23760      * @cfg {String|Roo.Template} tpl The template used by this View 
23761      */
23762     tpl : false,
23763     /**
23764      * @cfg {String} dataName the named area of the template to use as the data area
23765      *                          Works with domtemplates roo-name="name"
23766      */
23767     dataName: false,
23768     /**
23769      * @cfg {String} selectedClass The css class to add to selected nodes
23770      */
23771     selectedClass : "x-view-selected",
23772      /**
23773      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23774      */
23775     emptyText : "",
23776     /**
23777      * @cfg {Boolean} multiSelect Allow multiple selection
23778      */
23779     multiSelect : false,
23780     /**
23781      * @cfg {Boolean} singleSelect Allow single selection
23782      */
23783     singleSelect:  false,
23784     
23785     /**
23786      * @cfg {Boolean} toggleSelect - selecting 
23787      */
23788     toggleSelect : false,
23789     
23790     /**
23791      * Returns the element this view is bound to.
23792      * @return {Roo.Element}
23793      */
23794     getEl : function(){
23795         return this.el;
23796     },
23797
23798     /**
23799      * Refreshes the view.
23800      */
23801     refresh : function(){
23802         var t = this.tpl;
23803         
23804         // if we are using something like 'domtemplate', then
23805         // the what gets used is:
23806         // t.applySubtemplate(NAME, data, wrapping data..)
23807         // the outer template then get' applied with
23808         //     the store 'extra data'
23809         // and the body get's added to the
23810         //      roo-name="data" node?
23811         //      <span class='roo-tpl-{name}'></span> ?????
23812         
23813         
23814         
23815         this.clearSelections();
23816         this.el.update("");
23817         var html = [];
23818         var records = this.store.getRange();
23819         if(records.length < 1) {
23820             
23821             // is this valid??  = should it render a template??
23822             
23823             this.el.update(this.emptyText);
23824             return;
23825         }
23826         var el = this.el;
23827         if (this.dataName) {
23828             this.el.update(t.apply(this.store.meta)); //????
23829             el = this.el.child('.roo-tpl-' + this.dataName);
23830         }
23831         
23832         for(var i = 0, len = records.length; i < len; i++){
23833             var data = this.prepareData(records[i].data, i, records[i]);
23834             this.fireEvent("preparedata", this, data, i, records[i]);
23835             html[html.length] = Roo.util.Format.trim(
23836                 this.dataName ?
23837                     t.applySubtemplate(this.dataName, data, this.store.meta) :
23838                     t.apply(data)
23839             );
23840         }
23841         
23842         
23843         
23844         el.update(html.join(""));
23845         this.nodes = el.dom.childNodes;
23846         this.updateIndexes(0);
23847     },
23848
23849     /**
23850      * Function to override to reformat the data that is sent to
23851      * the template for each node.
23852      * DEPRICATED - use the preparedata event handler.
23853      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23854      * a JSON object for an UpdateManager bound view).
23855      */
23856     prepareData : function(data, index, record)
23857     {
23858         this.fireEvent("preparedata", this, data, index, record);
23859         return data;
23860     },
23861
23862     onUpdate : function(ds, record){
23863         this.clearSelections();
23864         var index = this.store.indexOf(record);
23865         var n = this.nodes[index];
23866         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
23867         n.parentNode.removeChild(n);
23868         this.updateIndexes(index, index);
23869     },
23870
23871     
23872     
23873 // --------- FIXME     
23874     onAdd : function(ds, records, index)
23875     {
23876         this.clearSelections();
23877         if(this.nodes.length == 0){
23878             this.refresh();
23879             return;
23880         }
23881         var n = this.nodes[index];
23882         for(var i = 0, len = records.length; i < len; i++){
23883             var d = this.prepareData(records[i].data, i, records[i]);
23884             if(n){
23885                 this.tpl.insertBefore(n, d);
23886             }else{
23887                 
23888                 this.tpl.append(this.el, d);
23889             }
23890         }
23891         this.updateIndexes(index);
23892     },
23893
23894     onRemove : function(ds, record, index){
23895         this.clearSelections();
23896         var el = this.dataName  ?
23897             this.el.child('.roo-tpl-' + this.dataName) :
23898             this.el; 
23899         el.dom.removeChild(this.nodes[index]);
23900         this.updateIndexes(index);
23901     },
23902
23903     /**
23904      * Refresh an individual node.
23905      * @param {Number} index
23906      */
23907     refreshNode : function(index){
23908         this.onUpdate(this.store, this.store.getAt(index));
23909     },
23910
23911     updateIndexes : function(startIndex, endIndex){
23912         var ns = this.nodes;
23913         startIndex = startIndex || 0;
23914         endIndex = endIndex || ns.length - 1;
23915         for(var i = startIndex; i <= endIndex; i++){
23916             ns[i].nodeIndex = i;
23917         }
23918     },
23919
23920     /**
23921      * Changes the data store this view uses and refresh the view.
23922      * @param {Store} store
23923      */
23924     setStore : function(store, initial){
23925         if(!initial && this.store){
23926             this.store.un("datachanged", this.refresh);
23927             this.store.un("add", this.onAdd);
23928             this.store.un("remove", this.onRemove);
23929             this.store.un("update", this.onUpdate);
23930             this.store.un("clear", this.refresh);
23931         }
23932         if(store){
23933           
23934             store.on("datachanged", this.refresh, this);
23935             store.on("add", this.onAdd, this);
23936             store.on("remove", this.onRemove, this);
23937             store.on("update", this.onUpdate, this);
23938             store.on("clear", this.refresh, this);
23939         }
23940         
23941         if(store){
23942             this.refresh();
23943         }
23944     },
23945
23946     /**
23947      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23948      * @param {HTMLElement} node
23949      * @return {HTMLElement} The template node
23950      */
23951     findItemFromChild : function(node){
23952         var el = this.dataName  ?
23953             this.el.child('.roo-tpl-' + this.dataName,true) :
23954             this.el.dom; 
23955         
23956         if(!node || node.parentNode == el){
23957                     return node;
23958             }
23959             var p = node.parentNode;
23960             while(p && p != el){
23961             if(p.parentNode == el){
23962                 return p;
23963             }
23964             p = p.parentNode;
23965         }
23966             return null;
23967     },
23968
23969     /** @ignore */
23970     onClick : function(e){
23971         var item = this.findItemFromChild(e.getTarget());
23972         if(item){
23973             var index = this.indexOf(item);
23974             if(this.onItemClick(item, index, e) !== false){
23975                 this.fireEvent("click", this, index, item, e);
23976             }
23977         }else{
23978             this.clearSelections();
23979         }
23980     },
23981
23982     /** @ignore */
23983     onContextMenu : function(e){
23984         var item = this.findItemFromChild(e.getTarget());
23985         if(item){
23986             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23987         }
23988     },
23989
23990     /** @ignore */
23991     onDblClick : function(e){
23992         var item = this.findItemFromChild(e.getTarget());
23993         if(item){
23994             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23995         }
23996     },
23997
23998     onItemClick : function(item, index, e)
23999     {
24000         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24001             return false;
24002         }
24003         if (this.toggleSelect) {
24004             var m = this.isSelected(item) ? 'unselect' : 'select';
24005             Roo.log(m);
24006             var _t = this;
24007             _t[m](item, true, false);
24008             return true;
24009         }
24010         if(this.multiSelect || this.singleSelect){
24011             if(this.multiSelect && e.shiftKey && this.lastSelection){
24012                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24013             }else{
24014                 this.select(item, this.multiSelect && e.ctrlKey);
24015                 this.lastSelection = item;
24016             }
24017             e.preventDefault();
24018         }
24019         return true;
24020     },
24021
24022     /**
24023      * Get the number of selected nodes.
24024      * @return {Number}
24025      */
24026     getSelectionCount : function(){
24027         return this.selections.length;
24028     },
24029
24030     /**
24031      * Get the currently selected nodes.
24032      * @return {Array} An array of HTMLElements
24033      */
24034     getSelectedNodes : function(){
24035         return this.selections;
24036     },
24037
24038     /**
24039      * Get the indexes of the selected nodes.
24040      * @return {Array}
24041      */
24042     getSelectedIndexes : function(){
24043         var indexes = [], s = this.selections;
24044         for(var i = 0, len = s.length; i < len; i++){
24045             indexes.push(s[i].nodeIndex);
24046         }
24047         return indexes;
24048     },
24049
24050     /**
24051      * Clear all selections
24052      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24053      */
24054     clearSelections : function(suppressEvent){
24055         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24056             this.cmp.elements = this.selections;
24057             this.cmp.removeClass(this.selectedClass);
24058             this.selections = [];
24059             if(!suppressEvent){
24060                 this.fireEvent("selectionchange", this, this.selections);
24061             }
24062         }
24063     },
24064
24065     /**
24066      * Returns true if the passed node is selected
24067      * @param {HTMLElement/Number} node The node or node index
24068      * @return {Boolean}
24069      */
24070     isSelected : function(node){
24071         var s = this.selections;
24072         if(s.length < 1){
24073             return false;
24074         }
24075         node = this.getNode(node);
24076         return s.indexOf(node) !== -1;
24077     },
24078
24079     /**
24080      * Selects nodes.
24081      * @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
24082      * @param {Boolean} keepExisting (optional) true to keep existing selections
24083      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24084      */
24085     select : function(nodeInfo, keepExisting, suppressEvent){
24086         if(nodeInfo instanceof Array){
24087             if(!keepExisting){
24088                 this.clearSelections(true);
24089             }
24090             for(var i = 0, len = nodeInfo.length; i < len; i++){
24091                 this.select(nodeInfo[i], true, true);
24092             }
24093             return;
24094         } 
24095         var node = this.getNode(nodeInfo);
24096         if(!node || this.isSelected(node)){
24097             return; // already selected.
24098         }
24099         if(!keepExisting){
24100             this.clearSelections(true);
24101         }
24102         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24103             Roo.fly(node).addClass(this.selectedClass);
24104             this.selections.push(node);
24105             if(!suppressEvent){
24106                 this.fireEvent("selectionchange", this, this.selections);
24107             }
24108         }
24109         
24110         
24111     },
24112       /**
24113      * Unselects nodes.
24114      * @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
24115      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24116      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24117      */
24118     unselect : function(nodeInfo, keepExisting, suppressEvent)
24119     {
24120         if(nodeInfo instanceof Array){
24121             Roo.each(this.selections, function(s) {
24122                 this.unselect(s, nodeInfo);
24123             }, this);
24124             return;
24125         }
24126         var node = this.getNode(nodeInfo);
24127         if(!node || !this.isSelected(node)){
24128             Roo.log("not selected");
24129             return; // not selected.
24130         }
24131         // fireevent???
24132         var ns = [];
24133         Roo.each(this.selections, function(s) {
24134             if (s == node ) {
24135                 Roo.fly(node).removeClass(this.selectedClass);
24136
24137                 return;
24138             }
24139             ns.push(s);
24140         },this);
24141         
24142         this.selections= ns;
24143         this.fireEvent("selectionchange", this, this.selections);
24144     },
24145
24146     /**
24147      * Gets a template node.
24148      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24149      * @return {HTMLElement} The node or null if it wasn't found
24150      */
24151     getNode : function(nodeInfo){
24152         if(typeof nodeInfo == "string"){
24153             return document.getElementById(nodeInfo);
24154         }else if(typeof nodeInfo == "number"){
24155             return this.nodes[nodeInfo];
24156         }
24157         return nodeInfo;
24158     },
24159
24160     /**
24161      * Gets a range template nodes.
24162      * @param {Number} startIndex
24163      * @param {Number} endIndex
24164      * @return {Array} An array of nodes
24165      */
24166     getNodes : function(start, end){
24167         var ns = this.nodes;
24168         start = start || 0;
24169         end = typeof end == "undefined" ? ns.length - 1 : end;
24170         var nodes = [];
24171         if(start <= end){
24172             for(var i = start; i <= end; i++){
24173                 nodes.push(ns[i]);
24174             }
24175         } else{
24176             for(var i = start; i >= end; i--){
24177                 nodes.push(ns[i]);
24178             }
24179         }
24180         return nodes;
24181     },
24182
24183     /**
24184      * Finds the index of the passed node
24185      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24186      * @return {Number} The index of the node or -1
24187      */
24188     indexOf : function(node){
24189         node = this.getNode(node);
24190         if(typeof node.nodeIndex == "number"){
24191             return node.nodeIndex;
24192         }
24193         var ns = this.nodes;
24194         for(var i = 0, len = ns.length; i < len; i++){
24195             if(ns[i] == node){
24196                 return i;
24197             }
24198         }
24199         return -1;
24200     }
24201 });
24202 /*
24203  * Based on:
24204  * Ext JS Library 1.1.1
24205  * Copyright(c) 2006-2007, Ext JS, LLC.
24206  *
24207  * Originally Released Under LGPL - original licence link has changed is not relivant.
24208  *
24209  * Fork - LGPL
24210  * <script type="text/javascript">
24211  */
24212
24213 /**
24214  * @class Roo.JsonView
24215  * @extends Roo.View
24216  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24217 <pre><code>
24218 var view = new Roo.JsonView({
24219     container: "my-element",
24220     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24221     multiSelect: true, 
24222     jsonRoot: "data" 
24223 });
24224
24225 // listen for node click?
24226 view.on("click", function(vw, index, node, e){
24227     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24228 });
24229
24230 // direct load of JSON data
24231 view.load("foobar.php");
24232
24233 // Example from my blog list
24234 var tpl = new Roo.Template(
24235     '&lt;div class="entry"&gt;' +
24236     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24237     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24238     "&lt;/div&gt;&lt;hr /&gt;"
24239 );
24240
24241 var moreView = new Roo.JsonView({
24242     container :  "entry-list", 
24243     template : tpl,
24244     jsonRoot: "posts"
24245 });
24246 moreView.on("beforerender", this.sortEntries, this);
24247 moreView.load({
24248     url: "/blog/get-posts.php",
24249     params: "allposts=true",
24250     text: "Loading Blog Entries..."
24251 });
24252 </code></pre>
24253
24254 * Note: old code is supported with arguments : (container, template, config)
24255
24256
24257  * @constructor
24258  * Create a new JsonView
24259  * 
24260  * @param {Object} config The config object
24261  * 
24262  */
24263 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24264     
24265     
24266     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24267
24268     var um = this.el.getUpdateManager();
24269     um.setRenderer(this);
24270     um.on("update", this.onLoad, this);
24271     um.on("failure", this.onLoadException, this);
24272
24273     /**
24274      * @event beforerender
24275      * Fires before rendering of the downloaded JSON data.
24276      * @param {Roo.JsonView} this
24277      * @param {Object} data The JSON data loaded
24278      */
24279     /**
24280      * @event load
24281      * Fires when data is loaded.
24282      * @param {Roo.JsonView} this
24283      * @param {Object} data The JSON data loaded
24284      * @param {Object} response The raw Connect response object
24285      */
24286     /**
24287      * @event loadexception
24288      * Fires when loading fails.
24289      * @param {Roo.JsonView} this
24290      * @param {Object} response The raw Connect response object
24291      */
24292     this.addEvents({
24293         'beforerender' : true,
24294         'load' : true,
24295         'loadexception' : true
24296     });
24297 };
24298 Roo.extend(Roo.JsonView, Roo.View, {
24299     /**
24300      * @type {String} The root property in the loaded JSON object that contains the data
24301      */
24302     jsonRoot : "",
24303
24304     /**
24305      * Refreshes the view.
24306      */
24307     refresh : function(){
24308         this.clearSelections();
24309         this.el.update("");
24310         var html = [];
24311         var o = this.jsonData;
24312         if(o && o.length > 0){
24313             for(var i = 0, len = o.length; i < len; i++){
24314                 var data = this.prepareData(o[i], i, o);
24315                 html[html.length] = this.tpl.apply(data);
24316             }
24317         }else{
24318             html.push(this.emptyText);
24319         }
24320         this.el.update(html.join(""));
24321         this.nodes = this.el.dom.childNodes;
24322         this.updateIndexes(0);
24323     },
24324
24325     /**
24326      * 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.
24327      * @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:
24328      <pre><code>
24329      view.load({
24330          url: "your-url.php",
24331          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24332          callback: yourFunction,
24333          scope: yourObject, //(optional scope)
24334          discardUrl: false,
24335          nocache: false,
24336          text: "Loading...",
24337          timeout: 30,
24338          scripts: false
24339      });
24340      </code></pre>
24341      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24342      * 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.
24343      * @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}
24344      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24345      * @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.
24346      */
24347     load : function(){
24348         var um = this.el.getUpdateManager();
24349         um.update.apply(um, arguments);
24350     },
24351
24352     render : function(el, response){
24353         this.clearSelections();
24354         this.el.update("");
24355         var o;
24356         try{
24357             o = Roo.util.JSON.decode(response.responseText);
24358             if(this.jsonRoot){
24359                 
24360                 o = o[this.jsonRoot];
24361             }
24362         } catch(e){
24363         }
24364         /**
24365          * The current JSON data or null
24366          */
24367         this.jsonData = o;
24368         this.beforeRender();
24369         this.refresh();
24370     },
24371
24372 /**
24373  * Get the number of records in the current JSON dataset
24374  * @return {Number}
24375  */
24376     getCount : function(){
24377         return this.jsonData ? this.jsonData.length : 0;
24378     },
24379
24380 /**
24381  * Returns the JSON object for the specified node(s)
24382  * @param {HTMLElement/Array} node The node or an array of nodes
24383  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24384  * you get the JSON object for the node
24385  */
24386     getNodeData : function(node){
24387         if(node instanceof Array){
24388             var data = [];
24389             for(var i = 0, len = node.length; i < len; i++){
24390                 data.push(this.getNodeData(node[i]));
24391             }
24392             return data;
24393         }
24394         return this.jsonData[this.indexOf(node)] || null;
24395     },
24396
24397     beforeRender : function(){
24398         this.snapshot = this.jsonData;
24399         if(this.sortInfo){
24400             this.sort.apply(this, this.sortInfo);
24401         }
24402         this.fireEvent("beforerender", this, this.jsonData);
24403     },
24404
24405     onLoad : function(el, o){
24406         this.fireEvent("load", this, this.jsonData, o);
24407     },
24408
24409     onLoadException : function(el, o){
24410         this.fireEvent("loadexception", this, o);
24411     },
24412
24413 /**
24414  * Filter the data by a specific property.
24415  * @param {String} property A property on your JSON objects
24416  * @param {String/RegExp} value Either string that the property values
24417  * should start with, or a RegExp to test against the property
24418  */
24419     filter : function(property, value){
24420         if(this.jsonData){
24421             var data = [];
24422             var ss = this.snapshot;
24423             if(typeof value == "string"){
24424                 var vlen = value.length;
24425                 if(vlen == 0){
24426                     this.clearFilter();
24427                     return;
24428                 }
24429                 value = value.toLowerCase();
24430                 for(var i = 0, len = ss.length; i < len; i++){
24431                     var o = ss[i];
24432                     if(o[property].substr(0, vlen).toLowerCase() == value){
24433                         data.push(o);
24434                     }
24435                 }
24436             } else if(value.exec){ // regex?
24437                 for(var i = 0, len = ss.length; i < len; i++){
24438                     var o = ss[i];
24439                     if(value.test(o[property])){
24440                         data.push(o);
24441                     }
24442                 }
24443             } else{
24444                 return;
24445             }
24446             this.jsonData = data;
24447             this.refresh();
24448         }
24449     },
24450
24451 /**
24452  * Filter by a function. The passed function will be called with each
24453  * object in the current dataset. If the function returns true the value is kept,
24454  * otherwise it is filtered.
24455  * @param {Function} fn
24456  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24457  */
24458     filterBy : function(fn, scope){
24459         if(this.jsonData){
24460             var data = [];
24461             var ss = this.snapshot;
24462             for(var i = 0, len = ss.length; i < len; i++){
24463                 var o = ss[i];
24464                 if(fn.call(scope || this, o)){
24465                     data.push(o);
24466                 }
24467             }
24468             this.jsonData = data;
24469             this.refresh();
24470         }
24471     },
24472
24473 /**
24474  * Clears the current filter.
24475  */
24476     clearFilter : function(){
24477         if(this.snapshot && this.jsonData != this.snapshot){
24478             this.jsonData = this.snapshot;
24479             this.refresh();
24480         }
24481     },
24482
24483
24484 /**
24485  * Sorts the data for this view and refreshes it.
24486  * @param {String} property A property on your JSON objects to sort on
24487  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24488  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24489  */
24490     sort : function(property, dir, sortType){
24491         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24492         if(this.jsonData){
24493             var p = property;
24494             var dsc = dir && dir.toLowerCase() == "desc";
24495             var f = function(o1, o2){
24496                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24497                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24498                 ;
24499                 if(v1 < v2){
24500                     return dsc ? +1 : -1;
24501                 } else if(v1 > v2){
24502                     return dsc ? -1 : +1;
24503                 } else{
24504                     return 0;
24505                 }
24506             };
24507             this.jsonData.sort(f);
24508             this.refresh();
24509             if(this.jsonData != this.snapshot){
24510                 this.snapshot.sort(f);
24511             }
24512         }
24513     }
24514 });/*
24515  * Based on:
24516  * Ext JS Library 1.1.1
24517  * Copyright(c) 2006-2007, Ext JS, LLC.
24518  *
24519  * Originally Released Under LGPL - original licence link has changed is not relivant.
24520  *
24521  * Fork - LGPL
24522  * <script type="text/javascript">
24523  */
24524  
24525
24526 /**
24527  * @class Roo.ColorPalette
24528  * @extends Roo.Component
24529  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24530  * Here's an example of typical usage:
24531  * <pre><code>
24532 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24533 cp.render('my-div');
24534
24535 cp.on('select', function(palette, selColor){
24536     // do something with selColor
24537 });
24538 </code></pre>
24539  * @constructor
24540  * Create a new ColorPalette
24541  * @param {Object} config The config object
24542  */
24543 Roo.ColorPalette = function(config){
24544     Roo.ColorPalette.superclass.constructor.call(this, config);
24545     this.addEvents({
24546         /**
24547              * @event select
24548              * Fires when a color is selected
24549              * @param {ColorPalette} this
24550              * @param {String} color The 6-digit color hex code (without the # symbol)
24551              */
24552         select: true
24553     });
24554
24555     if(this.handler){
24556         this.on("select", this.handler, this.scope, true);
24557     }
24558 };
24559 Roo.extend(Roo.ColorPalette, Roo.Component, {
24560     /**
24561      * @cfg {String} itemCls
24562      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24563      */
24564     itemCls : "x-color-palette",
24565     /**
24566      * @cfg {String} value
24567      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24568      * the hex codes are case-sensitive.
24569      */
24570     value : null,
24571     clickEvent:'click',
24572     // private
24573     ctype: "Roo.ColorPalette",
24574
24575     /**
24576      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24577      */
24578     allowReselect : false,
24579
24580     /**
24581      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24582      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24583      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24584      * of colors with the width setting until the box is symmetrical.</p>
24585      * <p>You can override individual colors if needed:</p>
24586      * <pre><code>
24587 var cp = new Roo.ColorPalette();
24588 cp.colors[0] = "FF0000";  // change the first box to red
24589 </code></pre>
24590
24591 Or you can provide a custom array of your own for complete control:
24592 <pre><code>
24593 var cp = new Roo.ColorPalette();
24594 cp.colors = ["000000", "993300", "333300"];
24595 </code></pre>
24596      * @type Array
24597      */
24598     colors : [
24599         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24600         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24601         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24602         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24603         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24604     ],
24605
24606     // private
24607     onRender : function(container, position){
24608         var t = new Roo.MasterTemplate(
24609             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24610         );
24611         var c = this.colors;
24612         for(var i = 0, len = c.length; i < len; i++){
24613             t.add([c[i]]);
24614         }
24615         var el = document.createElement("div");
24616         el.className = this.itemCls;
24617         t.overwrite(el);
24618         container.dom.insertBefore(el, position);
24619         this.el = Roo.get(el);
24620         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24621         if(this.clickEvent != 'click'){
24622             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24623         }
24624     },
24625
24626     // private
24627     afterRender : function(){
24628         Roo.ColorPalette.superclass.afterRender.call(this);
24629         if(this.value){
24630             var s = this.value;
24631             this.value = null;
24632             this.select(s);
24633         }
24634     },
24635
24636     // private
24637     handleClick : function(e, t){
24638         e.preventDefault();
24639         if(!this.disabled){
24640             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24641             this.select(c.toUpperCase());
24642         }
24643     },
24644
24645     /**
24646      * Selects the specified color in the palette (fires the select event)
24647      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24648      */
24649     select : function(color){
24650         color = color.replace("#", "");
24651         if(color != this.value || this.allowReselect){
24652             var el = this.el;
24653             if(this.value){
24654                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24655             }
24656             el.child("a.color-"+color).addClass("x-color-palette-sel");
24657             this.value = color;
24658             this.fireEvent("select", this, color);
24659         }
24660     }
24661 });/*
24662  * Based on:
24663  * Ext JS Library 1.1.1
24664  * Copyright(c) 2006-2007, Ext JS, LLC.
24665  *
24666  * Originally Released Under LGPL - original licence link has changed is not relivant.
24667  *
24668  * Fork - LGPL
24669  * <script type="text/javascript">
24670  */
24671  
24672 /**
24673  * @class Roo.DatePicker
24674  * @extends Roo.Component
24675  * Simple date picker class.
24676  * @constructor
24677  * Create a new DatePicker
24678  * @param {Object} config The config object
24679  */
24680 Roo.DatePicker = function(config){
24681     Roo.DatePicker.superclass.constructor.call(this, config);
24682
24683     this.value = config && config.value ?
24684                  config.value.clearTime() : new Date().clearTime();
24685
24686     this.addEvents({
24687         /**
24688              * @event select
24689              * Fires when a date is selected
24690              * @param {DatePicker} this
24691              * @param {Date} date The selected date
24692              */
24693         'select': true,
24694         /**
24695              * @event monthchange
24696              * Fires when the displayed month changes 
24697              * @param {DatePicker} this
24698              * @param {Date} date The selected month
24699              */
24700         'monthchange': true
24701     });
24702
24703     if(this.handler){
24704         this.on("select", this.handler,  this.scope || this);
24705     }
24706     // build the disabledDatesRE
24707     if(!this.disabledDatesRE && this.disabledDates){
24708         var dd = this.disabledDates;
24709         var re = "(?:";
24710         for(var i = 0; i < dd.length; i++){
24711             re += dd[i];
24712             if(i != dd.length-1) re += "|";
24713         }
24714         this.disabledDatesRE = new RegExp(re + ")");
24715     }
24716 };
24717
24718 Roo.extend(Roo.DatePicker, Roo.Component, {
24719     /**
24720      * @cfg {String} todayText
24721      * The text to display on the button that selects the current date (defaults to "Today")
24722      */
24723     todayText : "Today",
24724     /**
24725      * @cfg {String} okText
24726      * The text to display on the ok button
24727      */
24728     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24729     /**
24730      * @cfg {String} cancelText
24731      * The text to display on the cancel button
24732      */
24733     cancelText : "Cancel",
24734     /**
24735      * @cfg {String} todayTip
24736      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24737      */
24738     todayTip : "{0} (Spacebar)",
24739     /**
24740      * @cfg {Date} minDate
24741      * Minimum allowable date (JavaScript date object, defaults to null)
24742      */
24743     minDate : null,
24744     /**
24745      * @cfg {Date} maxDate
24746      * Maximum allowable date (JavaScript date object, defaults to null)
24747      */
24748     maxDate : null,
24749     /**
24750      * @cfg {String} minText
24751      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24752      */
24753     minText : "This date is before the minimum date",
24754     /**
24755      * @cfg {String} maxText
24756      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24757      */
24758     maxText : "This date is after the maximum date",
24759     /**
24760      * @cfg {String} format
24761      * The default date format string which can be overriden for localization support.  The format must be
24762      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24763      */
24764     format : "m/d/y",
24765     /**
24766      * @cfg {Array} disabledDays
24767      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24768      */
24769     disabledDays : null,
24770     /**
24771      * @cfg {String} disabledDaysText
24772      * The tooltip to display when the date falls on a disabled day (defaults to "")
24773      */
24774     disabledDaysText : "",
24775     /**
24776      * @cfg {RegExp} disabledDatesRE
24777      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24778      */
24779     disabledDatesRE : null,
24780     /**
24781      * @cfg {String} disabledDatesText
24782      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24783      */
24784     disabledDatesText : "",
24785     /**
24786      * @cfg {Boolean} constrainToViewport
24787      * True to constrain the date picker to the viewport (defaults to true)
24788      */
24789     constrainToViewport : true,
24790     /**
24791      * @cfg {Array} monthNames
24792      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24793      */
24794     monthNames : Date.monthNames,
24795     /**
24796      * @cfg {Array} dayNames
24797      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24798      */
24799     dayNames : Date.dayNames,
24800     /**
24801      * @cfg {String} nextText
24802      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24803      */
24804     nextText: 'Next Month (Control+Right)',
24805     /**
24806      * @cfg {String} prevText
24807      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24808      */
24809     prevText: 'Previous Month (Control+Left)',
24810     /**
24811      * @cfg {String} monthYearText
24812      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24813      */
24814     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24815     /**
24816      * @cfg {Number} startDay
24817      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24818      */
24819     startDay : 0,
24820     /**
24821      * @cfg {Bool} showClear
24822      * Show a clear button (usefull for date form elements that can be blank.)
24823      */
24824     
24825     showClear: false,
24826     
24827     /**
24828      * Sets the value of the date field
24829      * @param {Date} value The date to set
24830      */
24831     setValue : function(value){
24832         var old = this.value;
24833         this.value = value.clearTime(true);
24834         if(this.el){
24835             this.update(this.value);
24836         }
24837     },
24838
24839     /**
24840      * Gets the current selected value of the date field
24841      * @return {Date} The selected date
24842      */
24843     getValue : function(){
24844         return this.value;
24845     },
24846
24847     // private
24848     focus : function(){
24849         if(this.el){
24850             this.update(this.activeDate);
24851         }
24852     },
24853
24854     // private
24855     onRender : function(container, position){
24856         var m = [
24857              '<table cellspacing="0">',
24858                 '<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>',
24859                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24860         var dn = this.dayNames;
24861         for(var i = 0; i < 7; i++){
24862             var d = this.startDay+i;
24863             if(d > 6){
24864                 d = d-7;
24865             }
24866             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24867         }
24868         m[m.length] = "</tr></thead><tbody><tr>";
24869         for(var i = 0; i < 42; i++) {
24870             if(i % 7 == 0 && i != 0){
24871                 m[m.length] = "</tr><tr>";
24872             }
24873             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24874         }
24875         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24876             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24877
24878         var el = document.createElement("div");
24879         el.className = "x-date-picker";
24880         el.innerHTML = m.join("");
24881
24882         container.dom.insertBefore(el, position);
24883
24884         this.el = Roo.get(el);
24885         this.eventEl = Roo.get(el.firstChild);
24886
24887         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24888             handler: this.showPrevMonth,
24889             scope: this,
24890             preventDefault:true,
24891             stopDefault:true
24892         });
24893
24894         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24895             handler: this.showNextMonth,
24896             scope: this,
24897             preventDefault:true,
24898             stopDefault:true
24899         });
24900
24901         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24902
24903         this.monthPicker = this.el.down('div.x-date-mp');
24904         this.monthPicker.enableDisplayMode('block');
24905         
24906         var kn = new Roo.KeyNav(this.eventEl, {
24907             "left" : function(e){
24908                 e.ctrlKey ?
24909                     this.showPrevMonth() :
24910                     this.update(this.activeDate.add("d", -1));
24911             },
24912
24913             "right" : function(e){
24914                 e.ctrlKey ?
24915                     this.showNextMonth() :
24916                     this.update(this.activeDate.add("d", 1));
24917             },
24918
24919             "up" : function(e){
24920                 e.ctrlKey ?
24921                     this.showNextYear() :
24922                     this.update(this.activeDate.add("d", -7));
24923             },
24924
24925             "down" : function(e){
24926                 e.ctrlKey ?
24927                     this.showPrevYear() :
24928                     this.update(this.activeDate.add("d", 7));
24929             },
24930
24931             "pageUp" : function(e){
24932                 this.showNextMonth();
24933             },
24934
24935             "pageDown" : function(e){
24936                 this.showPrevMonth();
24937             },
24938
24939             "enter" : function(e){
24940                 e.stopPropagation();
24941                 return true;
24942             },
24943
24944             scope : this
24945         });
24946
24947         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24948
24949         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24950
24951         this.el.unselectable();
24952         
24953         this.cells = this.el.select("table.x-date-inner tbody td");
24954         this.textNodes = this.el.query("table.x-date-inner tbody span");
24955
24956         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24957             text: "&#160;",
24958             tooltip: this.monthYearText
24959         });
24960
24961         this.mbtn.on('click', this.showMonthPicker, this);
24962         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24963
24964
24965         var today = (new Date()).dateFormat(this.format);
24966         
24967         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24968         if (this.showClear) {
24969             baseTb.add( new Roo.Toolbar.Fill());
24970         }
24971         baseTb.add({
24972             text: String.format(this.todayText, today),
24973             tooltip: String.format(this.todayTip, today),
24974             handler: this.selectToday,
24975             scope: this
24976         });
24977         
24978         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24979             
24980         //});
24981         if (this.showClear) {
24982             
24983             baseTb.add( new Roo.Toolbar.Fill());
24984             baseTb.add({
24985                 text: '&#160;',
24986                 cls: 'x-btn-icon x-btn-clear',
24987                 handler: function() {
24988                     //this.value = '';
24989                     this.fireEvent("select", this, '');
24990                 },
24991                 scope: this
24992             });
24993         }
24994         
24995         
24996         if(Roo.isIE){
24997             this.el.repaint();
24998         }
24999         this.update(this.value);
25000     },
25001
25002     createMonthPicker : function(){
25003         if(!this.monthPicker.dom.firstChild){
25004             var buf = ['<table border="0" cellspacing="0">'];
25005             for(var i = 0; i < 6; i++){
25006                 buf.push(
25007                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25008                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25009                     i == 0 ?
25010                     '<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>' :
25011                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25012                 );
25013             }
25014             buf.push(
25015                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25016                     this.okText,
25017                     '</button><button type="button" class="x-date-mp-cancel">',
25018                     this.cancelText,
25019                     '</button></td></tr>',
25020                 '</table>'
25021             );
25022             this.monthPicker.update(buf.join(''));
25023             this.monthPicker.on('click', this.onMonthClick, this);
25024             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25025
25026             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25027             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25028
25029             this.mpMonths.each(function(m, a, i){
25030                 i += 1;
25031                 if((i%2) == 0){
25032                     m.dom.xmonth = 5 + Math.round(i * .5);
25033                 }else{
25034                     m.dom.xmonth = Math.round((i-1) * .5);
25035                 }
25036             });
25037         }
25038     },
25039
25040     showMonthPicker : function(){
25041         this.createMonthPicker();
25042         var size = this.el.getSize();
25043         this.monthPicker.setSize(size);
25044         this.monthPicker.child('table').setSize(size);
25045
25046         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25047         this.updateMPMonth(this.mpSelMonth);
25048         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25049         this.updateMPYear(this.mpSelYear);
25050
25051         this.monthPicker.slideIn('t', {duration:.2});
25052     },
25053
25054     updateMPYear : function(y){
25055         this.mpyear = y;
25056         var ys = this.mpYears.elements;
25057         for(var i = 1; i <= 10; i++){
25058             var td = ys[i-1], y2;
25059             if((i%2) == 0){
25060                 y2 = y + Math.round(i * .5);
25061                 td.firstChild.innerHTML = y2;
25062                 td.xyear = y2;
25063             }else{
25064                 y2 = y - (5-Math.round(i * .5));
25065                 td.firstChild.innerHTML = y2;
25066                 td.xyear = y2;
25067             }
25068             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25069         }
25070     },
25071
25072     updateMPMonth : function(sm){
25073         this.mpMonths.each(function(m, a, i){
25074             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25075         });
25076     },
25077
25078     selectMPMonth: function(m){
25079         
25080     },
25081
25082     onMonthClick : function(e, t){
25083         e.stopEvent();
25084         var el = new Roo.Element(t), pn;
25085         if(el.is('button.x-date-mp-cancel')){
25086             this.hideMonthPicker();
25087         }
25088         else if(el.is('button.x-date-mp-ok')){
25089             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25090             this.hideMonthPicker();
25091         }
25092         else if(pn = el.up('td.x-date-mp-month', 2)){
25093             this.mpMonths.removeClass('x-date-mp-sel');
25094             pn.addClass('x-date-mp-sel');
25095             this.mpSelMonth = pn.dom.xmonth;
25096         }
25097         else if(pn = el.up('td.x-date-mp-year', 2)){
25098             this.mpYears.removeClass('x-date-mp-sel');
25099             pn.addClass('x-date-mp-sel');
25100             this.mpSelYear = pn.dom.xyear;
25101         }
25102         else if(el.is('a.x-date-mp-prev')){
25103             this.updateMPYear(this.mpyear-10);
25104         }
25105         else if(el.is('a.x-date-mp-next')){
25106             this.updateMPYear(this.mpyear+10);
25107         }
25108     },
25109
25110     onMonthDblClick : function(e, t){
25111         e.stopEvent();
25112         var el = new Roo.Element(t), pn;
25113         if(pn = el.up('td.x-date-mp-month', 2)){
25114             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25115             this.hideMonthPicker();
25116         }
25117         else if(pn = el.up('td.x-date-mp-year', 2)){
25118             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25119             this.hideMonthPicker();
25120         }
25121     },
25122
25123     hideMonthPicker : function(disableAnim){
25124         if(this.monthPicker){
25125             if(disableAnim === true){
25126                 this.monthPicker.hide();
25127             }else{
25128                 this.monthPicker.slideOut('t', {duration:.2});
25129             }
25130         }
25131     },
25132
25133     // private
25134     showPrevMonth : function(e){
25135         this.update(this.activeDate.add("mo", -1));
25136     },
25137
25138     // private
25139     showNextMonth : function(e){
25140         this.update(this.activeDate.add("mo", 1));
25141     },
25142
25143     // private
25144     showPrevYear : function(){
25145         this.update(this.activeDate.add("y", -1));
25146     },
25147
25148     // private
25149     showNextYear : function(){
25150         this.update(this.activeDate.add("y", 1));
25151     },
25152
25153     // private
25154     handleMouseWheel : function(e){
25155         var delta = e.getWheelDelta();
25156         if(delta > 0){
25157             this.showPrevMonth();
25158             e.stopEvent();
25159         } else if(delta < 0){
25160             this.showNextMonth();
25161             e.stopEvent();
25162         }
25163     },
25164
25165     // private
25166     handleDateClick : function(e, t){
25167         e.stopEvent();
25168         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25169             this.setValue(new Date(t.dateValue));
25170             this.fireEvent("select", this, this.value);
25171         }
25172     },
25173
25174     // private
25175     selectToday : function(){
25176         this.setValue(new Date().clearTime());
25177         this.fireEvent("select", this, this.value);
25178     },
25179
25180     // private
25181     update : function(date)
25182     {
25183         var vd = this.activeDate;
25184         this.activeDate = date;
25185         if(vd && this.el){
25186             var t = date.getTime();
25187             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25188                 this.cells.removeClass("x-date-selected");
25189                 this.cells.each(function(c){
25190                    if(c.dom.firstChild.dateValue == t){
25191                        c.addClass("x-date-selected");
25192                        setTimeout(function(){
25193                             try{c.dom.firstChild.focus();}catch(e){}
25194                        }, 50);
25195                        return false;
25196                    }
25197                 });
25198                 return;
25199             }
25200         }
25201         
25202         var days = date.getDaysInMonth();
25203         var firstOfMonth = date.getFirstDateOfMonth();
25204         var startingPos = firstOfMonth.getDay()-this.startDay;
25205
25206         if(startingPos <= this.startDay){
25207             startingPos += 7;
25208         }
25209
25210         var pm = date.add("mo", -1);
25211         var prevStart = pm.getDaysInMonth()-startingPos;
25212
25213         var cells = this.cells.elements;
25214         var textEls = this.textNodes;
25215         days += startingPos;
25216
25217         // convert everything to numbers so it's fast
25218         var day = 86400000;
25219         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25220         var today = new Date().clearTime().getTime();
25221         var sel = date.clearTime().getTime();
25222         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25223         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25224         var ddMatch = this.disabledDatesRE;
25225         var ddText = this.disabledDatesText;
25226         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25227         var ddaysText = this.disabledDaysText;
25228         var format = this.format;
25229
25230         var setCellClass = function(cal, cell){
25231             cell.title = "";
25232             var t = d.getTime();
25233             cell.firstChild.dateValue = t;
25234             if(t == today){
25235                 cell.className += " x-date-today";
25236                 cell.title = cal.todayText;
25237             }
25238             if(t == sel){
25239                 cell.className += " x-date-selected";
25240                 setTimeout(function(){
25241                     try{cell.firstChild.focus();}catch(e){}
25242                 }, 50);
25243             }
25244             // disabling
25245             if(t < min) {
25246                 cell.className = " x-date-disabled";
25247                 cell.title = cal.minText;
25248                 return;
25249             }
25250             if(t > max) {
25251                 cell.className = " x-date-disabled";
25252                 cell.title = cal.maxText;
25253                 return;
25254             }
25255             if(ddays){
25256                 if(ddays.indexOf(d.getDay()) != -1){
25257                     cell.title = ddaysText;
25258                     cell.className = " x-date-disabled";
25259                 }
25260             }
25261             if(ddMatch && format){
25262                 var fvalue = d.dateFormat(format);
25263                 if(ddMatch.test(fvalue)){
25264                     cell.title = ddText.replace("%0", fvalue);
25265                     cell.className = " x-date-disabled";
25266                 }
25267             }
25268         };
25269
25270         var i = 0;
25271         for(; i < startingPos; i++) {
25272             textEls[i].innerHTML = (++prevStart);
25273             d.setDate(d.getDate()+1);
25274             cells[i].className = "x-date-prevday";
25275             setCellClass(this, cells[i]);
25276         }
25277         for(; i < days; i++){
25278             intDay = i - startingPos + 1;
25279             textEls[i].innerHTML = (intDay);
25280             d.setDate(d.getDate()+1);
25281             cells[i].className = "x-date-active";
25282             setCellClass(this, cells[i]);
25283         }
25284         var extraDays = 0;
25285         for(; i < 42; i++) {
25286              textEls[i].innerHTML = (++extraDays);
25287              d.setDate(d.getDate()+1);
25288              cells[i].className = "x-date-nextday";
25289              setCellClass(this, cells[i]);
25290         }
25291
25292         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25293         this.fireEvent('monthchange', this, date);
25294         
25295         if(!this.internalRender){
25296             var main = this.el.dom.firstChild;
25297             var w = main.offsetWidth;
25298             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25299             Roo.fly(main).setWidth(w);
25300             this.internalRender = true;
25301             // opera does not respect the auto grow header center column
25302             // then, after it gets a width opera refuses to recalculate
25303             // without a second pass
25304             if(Roo.isOpera && !this.secondPass){
25305                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25306                 this.secondPass = true;
25307                 this.update.defer(10, this, [date]);
25308             }
25309         }
25310         
25311         
25312     }
25313 });        /*
25314  * Based on:
25315  * Ext JS Library 1.1.1
25316  * Copyright(c) 2006-2007, Ext JS, LLC.
25317  *
25318  * Originally Released Under LGPL - original licence link has changed is not relivant.
25319  *
25320  * Fork - LGPL
25321  * <script type="text/javascript">
25322  */
25323 /**
25324  * @class Roo.TabPanel
25325  * @extends Roo.util.Observable
25326  * A lightweight tab container.
25327  * <br><br>
25328  * Usage:
25329  * <pre><code>
25330 // basic tabs 1, built from existing content
25331 var tabs = new Roo.TabPanel("tabs1");
25332 tabs.addTab("script", "View Script");
25333 tabs.addTab("markup", "View Markup");
25334 tabs.activate("script");
25335
25336 // more advanced tabs, built from javascript
25337 var jtabs = new Roo.TabPanel("jtabs");
25338 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25339
25340 // set up the UpdateManager
25341 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25342 var updater = tab2.getUpdateManager();
25343 updater.setDefaultUrl("ajax1.htm");
25344 tab2.on('activate', updater.refresh, updater, true);
25345
25346 // Use setUrl for Ajax loading
25347 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25348 tab3.setUrl("ajax2.htm", null, true);
25349
25350 // Disabled tab
25351 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25352 tab4.disable();
25353
25354 jtabs.activate("jtabs-1");
25355  * </code></pre>
25356  * @constructor
25357  * Create a new TabPanel.
25358  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25359  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25360  */
25361 Roo.TabPanel = function(container, config){
25362     /**
25363     * The container element for this TabPanel.
25364     * @type Roo.Element
25365     */
25366     this.el = Roo.get(container, true);
25367     if(config){
25368         if(typeof config == "boolean"){
25369             this.tabPosition = config ? "bottom" : "top";
25370         }else{
25371             Roo.apply(this, config);
25372         }
25373     }
25374     if(this.tabPosition == "bottom"){
25375         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25376         this.el.addClass("x-tabs-bottom");
25377     }
25378     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25379     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25380     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25381     if(Roo.isIE){
25382         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25383     }
25384     if(this.tabPosition != "bottom"){
25385         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25386          * @type Roo.Element
25387          */
25388         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25389         this.el.addClass("x-tabs-top");
25390     }
25391     this.items = [];
25392
25393     this.bodyEl.setStyle("position", "relative");
25394
25395     this.active = null;
25396     this.activateDelegate = this.activate.createDelegate(this);
25397
25398     this.addEvents({
25399         /**
25400          * @event tabchange
25401          * Fires when the active tab changes
25402          * @param {Roo.TabPanel} this
25403          * @param {Roo.TabPanelItem} activePanel The new active tab
25404          */
25405         "tabchange": true,
25406         /**
25407          * @event beforetabchange
25408          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25409          * @param {Roo.TabPanel} this
25410          * @param {Object} e Set cancel to true on this object to cancel the tab change
25411          * @param {Roo.TabPanelItem} tab The tab being changed to
25412          */
25413         "beforetabchange" : true
25414     });
25415
25416     Roo.EventManager.onWindowResize(this.onResize, this);
25417     this.cpad = this.el.getPadding("lr");
25418     this.hiddenCount = 0;
25419
25420
25421     // toolbar on the tabbar support...
25422     if (this.toolbar) {
25423         var tcfg = this.toolbar;
25424         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25425         this.toolbar = new Roo.Toolbar(tcfg);
25426         if (Roo.isSafari) {
25427             var tbl = tcfg.container.child('table', true);
25428             tbl.setAttribute('width', '100%');
25429         }
25430         
25431     }
25432    
25433
25434
25435     Roo.TabPanel.superclass.constructor.call(this);
25436 };
25437
25438 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25439     /*
25440      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25441      */
25442     tabPosition : "top",
25443     /*
25444      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25445      */
25446     currentTabWidth : 0,
25447     /*
25448      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25449      */
25450     minTabWidth : 40,
25451     /*
25452      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25453      */
25454     maxTabWidth : 250,
25455     /*
25456      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25457      */
25458     preferredTabWidth : 175,
25459     /*
25460      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25461      */
25462     resizeTabs : false,
25463     /*
25464      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25465      */
25466     monitorResize : true,
25467     /*
25468      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25469      */
25470     toolbar : false,
25471
25472     /**
25473      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25474      * @param {String} id The id of the div to use <b>or create</b>
25475      * @param {String} text The text for the tab
25476      * @param {String} content (optional) Content to put in the TabPanelItem body
25477      * @param {Boolean} closable (optional) True to create a close icon on the tab
25478      * @return {Roo.TabPanelItem} The created TabPanelItem
25479      */
25480     addTab : function(id, text, content, closable){
25481         var item = new Roo.TabPanelItem(this, id, text, closable);
25482         this.addTabItem(item);
25483         if(content){
25484             item.setContent(content);
25485         }
25486         return item;
25487     },
25488
25489     /**
25490      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25491      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25492      * @return {Roo.TabPanelItem}
25493      */
25494     getTab : function(id){
25495         return this.items[id];
25496     },
25497
25498     /**
25499      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25500      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25501      */
25502     hideTab : function(id){
25503         var t = this.items[id];
25504         if(!t.isHidden()){
25505            t.setHidden(true);
25506            this.hiddenCount++;
25507            this.autoSizeTabs();
25508         }
25509     },
25510
25511     /**
25512      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25513      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25514      */
25515     unhideTab : function(id){
25516         var t = this.items[id];
25517         if(t.isHidden()){
25518            t.setHidden(false);
25519            this.hiddenCount--;
25520            this.autoSizeTabs();
25521         }
25522     },
25523
25524     /**
25525      * Adds an existing {@link Roo.TabPanelItem}.
25526      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25527      */
25528     addTabItem : function(item){
25529         this.items[item.id] = item;
25530         this.items.push(item);
25531         if(this.resizeTabs){
25532            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25533            this.autoSizeTabs();
25534         }else{
25535             item.autoSize();
25536         }
25537     },
25538
25539     /**
25540      * Removes a {@link Roo.TabPanelItem}.
25541      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25542      */
25543     removeTab : function(id){
25544         var items = this.items;
25545         var tab = items[id];
25546         if(!tab) { return; }
25547         var index = items.indexOf(tab);
25548         if(this.active == tab && items.length > 1){
25549             var newTab = this.getNextAvailable(index);
25550             if(newTab) {
25551                 newTab.activate();
25552             }
25553         }
25554         this.stripEl.dom.removeChild(tab.pnode.dom);
25555         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25556             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25557         }
25558         items.splice(index, 1);
25559         delete this.items[tab.id];
25560         tab.fireEvent("close", tab);
25561         tab.purgeListeners();
25562         this.autoSizeTabs();
25563     },
25564
25565     getNextAvailable : function(start){
25566         var items = this.items;
25567         var index = start;
25568         // look for a next tab that will slide over to
25569         // replace the one being removed
25570         while(index < items.length){
25571             var item = items[++index];
25572             if(item && !item.isHidden()){
25573                 return item;
25574             }
25575         }
25576         // if one isn't found select the previous tab (on the left)
25577         index = start;
25578         while(index >= 0){
25579             var item = items[--index];
25580             if(item && !item.isHidden()){
25581                 return item;
25582             }
25583         }
25584         return null;
25585     },
25586
25587     /**
25588      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25589      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25590      */
25591     disableTab : function(id){
25592         var tab = this.items[id];
25593         if(tab && this.active != tab){
25594             tab.disable();
25595         }
25596     },
25597
25598     /**
25599      * Enables a {@link Roo.TabPanelItem} that is disabled.
25600      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25601      */
25602     enableTab : function(id){
25603         var tab = this.items[id];
25604         tab.enable();
25605     },
25606
25607     /**
25608      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25609      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25610      * @return {Roo.TabPanelItem} The TabPanelItem.
25611      */
25612     activate : function(id){
25613         var tab = this.items[id];
25614         if(!tab){
25615             return null;
25616         }
25617         if(tab == this.active || tab.disabled){
25618             return tab;
25619         }
25620         var e = {};
25621         this.fireEvent("beforetabchange", this, e, tab);
25622         if(e.cancel !== true && !tab.disabled){
25623             if(this.active){
25624                 this.active.hide();
25625             }
25626             this.active = this.items[id];
25627             this.active.show();
25628             this.fireEvent("tabchange", this, this.active);
25629         }
25630         return tab;
25631     },
25632
25633     /**
25634      * Gets the active {@link Roo.TabPanelItem}.
25635      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25636      */
25637     getActiveTab : function(){
25638         return this.active;
25639     },
25640
25641     /**
25642      * Updates the tab body element to fit the height of the container element
25643      * for overflow scrolling
25644      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25645      */
25646     syncHeight : function(targetHeight){
25647         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25648         var bm = this.bodyEl.getMargins();
25649         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25650         this.bodyEl.setHeight(newHeight);
25651         return newHeight;
25652     },
25653
25654     onResize : function(){
25655         if(this.monitorResize){
25656             this.autoSizeTabs();
25657         }
25658     },
25659
25660     /**
25661      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25662      */
25663     beginUpdate : function(){
25664         this.updating = true;
25665     },
25666
25667     /**
25668      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25669      */
25670     endUpdate : function(){
25671         this.updating = false;
25672         this.autoSizeTabs();
25673     },
25674
25675     /**
25676      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25677      */
25678     autoSizeTabs : function(){
25679         var count = this.items.length;
25680         var vcount = count - this.hiddenCount;
25681         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25682         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25683         var availWidth = Math.floor(w / vcount);
25684         var b = this.stripBody;
25685         if(b.getWidth() > w){
25686             var tabs = this.items;
25687             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25688             if(availWidth < this.minTabWidth){
25689                 /*if(!this.sleft){    // incomplete scrolling code
25690                     this.createScrollButtons();
25691                 }
25692                 this.showScroll();
25693                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25694             }
25695         }else{
25696             if(this.currentTabWidth < this.preferredTabWidth){
25697                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25698             }
25699         }
25700     },
25701
25702     /**
25703      * Returns the number of tabs in this TabPanel.
25704      * @return {Number}
25705      */
25706      getCount : function(){
25707          return this.items.length;
25708      },
25709
25710     /**
25711      * Resizes all the tabs to the passed width
25712      * @param {Number} The new width
25713      */
25714     setTabWidth : function(width){
25715         this.currentTabWidth = width;
25716         for(var i = 0, len = this.items.length; i < len; i++) {
25717                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25718         }
25719     },
25720
25721     /**
25722      * Destroys this TabPanel
25723      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25724      */
25725     destroy : function(removeEl){
25726         Roo.EventManager.removeResizeListener(this.onResize, this);
25727         for(var i = 0, len = this.items.length; i < len; i++){
25728             this.items[i].purgeListeners();
25729         }
25730         if(removeEl === true){
25731             this.el.update("");
25732             this.el.remove();
25733         }
25734     }
25735 });
25736
25737 /**
25738  * @class Roo.TabPanelItem
25739  * @extends Roo.util.Observable
25740  * Represents an individual item (tab plus body) in a TabPanel.
25741  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25742  * @param {String} id The id of this TabPanelItem
25743  * @param {String} text The text for the tab of this TabPanelItem
25744  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25745  */
25746 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25747     /**
25748      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25749      * @type Roo.TabPanel
25750      */
25751     this.tabPanel = tabPanel;
25752     /**
25753      * The id for this TabPanelItem
25754      * @type String
25755      */
25756     this.id = id;
25757     /** @private */
25758     this.disabled = false;
25759     /** @private */
25760     this.text = text;
25761     /** @private */
25762     this.loaded = false;
25763     this.closable = closable;
25764
25765     /**
25766      * The body element for this TabPanelItem.
25767      * @type Roo.Element
25768      */
25769     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25770     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25771     this.bodyEl.setStyle("display", "block");
25772     this.bodyEl.setStyle("zoom", "1");
25773     this.hideAction();
25774
25775     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25776     /** @private */
25777     this.el = Roo.get(els.el, true);
25778     this.inner = Roo.get(els.inner, true);
25779     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25780     this.pnode = Roo.get(els.el.parentNode, true);
25781     this.el.on("mousedown", this.onTabMouseDown, this);
25782     this.el.on("click", this.onTabClick, this);
25783     /** @private */
25784     if(closable){
25785         var c = Roo.get(els.close, true);
25786         c.dom.title = this.closeText;
25787         c.addClassOnOver("close-over");
25788         c.on("click", this.closeClick, this);
25789      }
25790
25791     this.addEvents({
25792          /**
25793          * @event activate
25794          * Fires when this tab becomes the active tab.
25795          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25796          * @param {Roo.TabPanelItem} this
25797          */
25798         "activate": true,
25799         /**
25800          * @event beforeclose
25801          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25802          * @param {Roo.TabPanelItem} this
25803          * @param {Object} e Set cancel to true on this object to cancel the close.
25804          */
25805         "beforeclose": true,
25806         /**
25807          * @event close
25808          * Fires when this tab is closed.
25809          * @param {Roo.TabPanelItem} this
25810          */
25811          "close": true,
25812         /**
25813          * @event deactivate
25814          * Fires when this tab is no longer the active tab.
25815          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25816          * @param {Roo.TabPanelItem} this
25817          */
25818          "deactivate" : true
25819     });
25820     this.hidden = false;
25821
25822     Roo.TabPanelItem.superclass.constructor.call(this);
25823 };
25824
25825 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25826     purgeListeners : function(){
25827        Roo.util.Observable.prototype.purgeListeners.call(this);
25828        this.el.removeAllListeners();
25829     },
25830     /**
25831      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25832      */
25833     show : function(){
25834         this.pnode.addClass("on");
25835         this.showAction();
25836         if(Roo.isOpera){
25837             this.tabPanel.stripWrap.repaint();
25838         }
25839         this.fireEvent("activate", this.tabPanel, this);
25840     },
25841
25842     /**
25843      * Returns true if this tab is the active tab.
25844      * @return {Boolean}
25845      */
25846     isActive : function(){
25847         return this.tabPanel.getActiveTab() == this;
25848     },
25849
25850     /**
25851      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25852      */
25853     hide : function(){
25854         this.pnode.removeClass("on");
25855         this.hideAction();
25856         this.fireEvent("deactivate", this.tabPanel, this);
25857     },
25858
25859     hideAction : function(){
25860         this.bodyEl.hide();
25861         this.bodyEl.setStyle("position", "absolute");
25862         this.bodyEl.setLeft("-20000px");
25863         this.bodyEl.setTop("-20000px");
25864     },
25865
25866     showAction : function(){
25867         this.bodyEl.setStyle("position", "relative");
25868         this.bodyEl.setTop("");
25869         this.bodyEl.setLeft("");
25870         this.bodyEl.show();
25871     },
25872
25873     /**
25874      * Set the tooltip for the tab.
25875      * @param {String} tooltip The tab's tooltip
25876      */
25877     setTooltip : function(text){
25878         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25879             this.textEl.dom.qtip = text;
25880             this.textEl.dom.removeAttribute('title');
25881         }else{
25882             this.textEl.dom.title = text;
25883         }
25884     },
25885
25886     onTabClick : function(e){
25887         e.preventDefault();
25888         this.tabPanel.activate(this.id);
25889     },
25890
25891     onTabMouseDown : function(e){
25892         e.preventDefault();
25893         this.tabPanel.activate(this.id);
25894     },
25895
25896     getWidth : function(){
25897         return this.inner.getWidth();
25898     },
25899
25900     setWidth : function(width){
25901         var iwidth = width - this.pnode.getPadding("lr");
25902         this.inner.setWidth(iwidth);
25903         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25904         this.pnode.setWidth(width);
25905     },
25906
25907     /**
25908      * Show or hide the tab
25909      * @param {Boolean} hidden True to hide or false to show.
25910      */
25911     setHidden : function(hidden){
25912         this.hidden = hidden;
25913         this.pnode.setStyle("display", hidden ? "none" : "");
25914     },
25915
25916     /**
25917      * Returns true if this tab is "hidden"
25918      * @return {Boolean}
25919      */
25920     isHidden : function(){
25921         return this.hidden;
25922     },
25923
25924     /**
25925      * Returns the text for this tab
25926      * @return {String}
25927      */
25928     getText : function(){
25929         return this.text;
25930     },
25931
25932     autoSize : function(){
25933         //this.el.beginMeasure();
25934         this.textEl.setWidth(1);
25935         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25936         //this.el.endMeasure();
25937     },
25938
25939     /**
25940      * Sets the text for the tab (Note: this also sets the tooltip text)
25941      * @param {String} text The tab's text and tooltip
25942      */
25943     setText : function(text){
25944         this.text = text;
25945         this.textEl.update(text);
25946         this.setTooltip(text);
25947         if(!this.tabPanel.resizeTabs){
25948             this.autoSize();
25949         }
25950     },
25951     /**
25952      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25953      */
25954     activate : function(){
25955         this.tabPanel.activate(this.id);
25956     },
25957
25958     /**
25959      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25960      */
25961     disable : function(){
25962         if(this.tabPanel.active != this){
25963             this.disabled = true;
25964             this.pnode.addClass("disabled");
25965         }
25966     },
25967
25968     /**
25969      * Enables this TabPanelItem if it was previously disabled.
25970      */
25971     enable : function(){
25972         this.disabled = false;
25973         this.pnode.removeClass("disabled");
25974     },
25975
25976     /**
25977      * Sets the content for this TabPanelItem.
25978      * @param {String} content The content
25979      * @param {Boolean} loadScripts true to look for and load scripts
25980      */
25981     setContent : function(content, loadScripts){
25982         this.bodyEl.update(content, loadScripts);
25983     },
25984
25985     /**
25986      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25987      * @return {Roo.UpdateManager} The UpdateManager
25988      */
25989     getUpdateManager : function(){
25990         return this.bodyEl.getUpdateManager();
25991     },
25992
25993     /**
25994      * Set a URL to be used to load the content for this TabPanelItem.
25995      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25996      * @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)
25997      * @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)
25998      * @return {Roo.UpdateManager} The UpdateManager
25999      */
26000     setUrl : function(url, params, loadOnce){
26001         if(this.refreshDelegate){
26002             this.un('activate', this.refreshDelegate);
26003         }
26004         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26005         this.on("activate", this.refreshDelegate);
26006         return this.bodyEl.getUpdateManager();
26007     },
26008
26009     /** @private */
26010     _handleRefresh : function(url, params, loadOnce){
26011         if(!loadOnce || !this.loaded){
26012             var updater = this.bodyEl.getUpdateManager();
26013             updater.update(url, params, this._setLoaded.createDelegate(this));
26014         }
26015     },
26016
26017     /**
26018      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26019      *   Will fail silently if the setUrl method has not been called.
26020      *   This does not activate the panel, just updates its content.
26021      */
26022     refresh : function(){
26023         if(this.refreshDelegate){
26024            this.loaded = false;
26025            this.refreshDelegate();
26026         }
26027     },
26028
26029     /** @private */
26030     _setLoaded : function(){
26031         this.loaded = true;
26032     },
26033
26034     /** @private */
26035     closeClick : function(e){
26036         var o = {};
26037         e.stopEvent();
26038         this.fireEvent("beforeclose", this, o);
26039         if(o.cancel !== true){
26040             this.tabPanel.removeTab(this.id);
26041         }
26042     },
26043     /**
26044      * The text displayed in the tooltip for the close icon.
26045      * @type String
26046      */
26047     closeText : "Close this tab"
26048 });
26049
26050 /** @private */
26051 Roo.TabPanel.prototype.createStrip = function(container){
26052     var strip = document.createElement("div");
26053     strip.className = "x-tabs-wrap";
26054     container.appendChild(strip);
26055     return strip;
26056 };
26057 /** @private */
26058 Roo.TabPanel.prototype.createStripList = function(strip){
26059     // div wrapper for retard IE
26060     // returns the "tr" element.
26061     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26062         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26063         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26064     return strip.firstChild.firstChild.firstChild.firstChild;
26065 };
26066 /** @private */
26067 Roo.TabPanel.prototype.createBody = function(container){
26068     var body = document.createElement("div");
26069     Roo.id(body, "tab-body");
26070     Roo.fly(body).addClass("x-tabs-body");
26071     container.appendChild(body);
26072     return body;
26073 };
26074 /** @private */
26075 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26076     var body = Roo.getDom(id);
26077     if(!body){
26078         body = document.createElement("div");
26079         body.id = id;
26080     }
26081     Roo.fly(body).addClass("x-tabs-item-body");
26082     bodyEl.insertBefore(body, bodyEl.firstChild);
26083     return body;
26084 };
26085 /** @private */
26086 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26087     var td = document.createElement("td");
26088     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26089     //stripEl.appendChild(td);
26090     if(closable){
26091         td.className = "x-tabs-closable";
26092         if(!this.closeTpl){
26093             this.closeTpl = new Roo.Template(
26094                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26095                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26096                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26097             );
26098         }
26099         var el = this.closeTpl.overwrite(td, {"text": text});
26100         var close = el.getElementsByTagName("div")[0];
26101         var inner = el.getElementsByTagName("em")[0];
26102         return {"el": el, "close": close, "inner": inner};
26103     } else {
26104         if(!this.tabTpl){
26105             this.tabTpl = new Roo.Template(
26106                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26107                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26108             );
26109         }
26110         var el = this.tabTpl.overwrite(td, {"text": text});
26111         var inner = el.getElementsByTagName("em")[0];
26112         return {"el": el, "inner": inner};
26113     }
26114 };/*
26115  * Based on:
26116  * Ext JS Library 1.1.1
26117  * Copyright(c) 2006-2007, Ext JS, LLC.
26118  *
26119  * Originally Released Under LGPL - original licence link has changed is not relivant.
26120  *
26121  * Fork - LGPL
26122  * <script type="text/javascript">
26123  */
26124
26125 /**
26126  * @class Roo.Button
26127  * @extends Roo.util.Observable
26128  * Simple Button class
26129  * @cfg {String} text The button text
26130  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26131  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26132  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26133  * @cfg {Object} scope The scope of the handler
26134  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26135  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26136  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26137  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26138  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26139  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26140    applies if enableToggle = true)
26141  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26142  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26143   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26144  * @constructor
26145  * Create a new button
26146  * @param {Object} config The config object
26147  */
26148 Roo.Button = function(renderTo, config)
26149 {
26150     if (!config) {
26151         config = renderTo;
26152         renderTo = config.renderTo || false;
26153     }
26154     
26155     Roo.apply(this, config);
26156     this.addEvents({
26157         /**
26158              * @event click
26159              * Fires when this button is clicked
26160              * @param {Button} this
26161              * @param {EventObject} e The click event
26162              */
26163             "click" : true,
26164         /**
26165              * @event toggle
26166              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26167              * @param {Button} this
26168              * @param {Boolean} pressed
26169              */
26170             "toggle" : true,
26171         /**
26172              * @event mouseover
26173              * Fires when the mouse hovers over the button
26174              * @param {Button} this
26175              * @param {Event} e The event object
26176              */
26177         'mouseover' : true,
26178         /**
26179              * @event mouseout
26180              * Fires when the mouse exits the button
26181              * @param {Button} this
26182              * @param {Event} e The event object
26183              */
26184         'mouseout': true,
26185          /**
26186              * @event render
26187              * Fires when the button is rendered
26188              * @param {Button} this
26189              */
26190         'render': true
26191     });
26192     if(this.menu){
26193         this.menu = Roo.menu.MenuMgr.get(this.menu);
26194     }
26195     // register listeners first!!  - so render can be captured..
26196     Roo.util.Observable.call(this);
26197     if(renderTo){
26198         this.render(renderTo);
26199     }
26200     
26201   
26202 };
26203
26204 Roo.extend(Roo.Button, Roo.util.Observable, {
26205     /**
26206      * 
26207      */
26208     
26209     /**
26210      * Read-only. True if this button is hidden
26211      * @type Boolean
26212      */
26213     hidden : false,
26214     /**
26215      * Read-only. True if this button is disabled
26216      * @type Boolean
26217      */
26218     disabled : false,
26219     /**
26220      * Read-only. True if this button is pressed (only if enableToggle = true)
26221      * @type Boolean
26222      */
26223     pressed : false,
26224
26225     /**
26226      * @cfg {Number} tabIndex 
26227      * The DOM tabIndex for this button (defaults to undefined)
26228      */
26229     tabIndex : undefined,
26230
26231     /**
26232      * @cfg {Boolean} enableToggle
26233      * True to enable pressed/not pressed toggling (defaults to false)
26234      */
26235     enableToggle: false,
26236     /**
26237      * @cfg {Mixed} menu
26238      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26239      */
26240     menu : undefined,
26241     /**
26242      * @cfg {String} menuAlign
26243      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26244      */
26245     menuAlign : "tl-bl?",
26246
26247     /**
26248      * @cfg {String} iconCls
26249      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26250      */
26251     iconCls : undefined,
26252     /**
26253      * @cfg {String} type
26254      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26255      */
26256     type : 'button',
26257
26258     // private
26259     menuClassTarget: 'tr',
26260
26261     /**
26262      * @cfg {String} clickEvent
26263      * The type of event to map to the button's event handler (defaults to 'click')
26264      */
26265     clickEvent : 'click',
26266
26267     /**
26268      * @cfg {Boolean} handleMouseEvents
26269      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26270      */
26271     handleMouseEvents : true,
26272
26273     /**
26274      * @cfg {String} tooltipType
26275      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26276      */
26277     tooltipType : 'qtip',
26278
26279     /**
26280      * @cfg {String} cls
26281      * A CSS class to apply to the button's main element.
26282      */
26283     
26284     /**
26285      * @cfg {Roo.Template} template (Optional)
26286      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26287      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26288      * require code modifications if required elements (e.g. a button) aren't present.
26289      */
26290
26291     // private
26292     render : function(renderTo){
26293         var btn;
26294         if(this.hideParent){
26295             this.parentEl = Roo.get(renderTo);
26296         }
26297         if(!this.dhconfig){
26298             if(!this.template){
26299                 if(!Roo.Button.buttonTemplate){
26300                     // hideous table template
26301                     Roo.Button.buttonTemplate = new Roo.Template(
26302                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26303                         '<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>',
26304                         "</tr></tbody></table>");
26305                 }
26306                 this.template = Roo.Button.buttonTemplate;
26307             }
26308             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26309             var btnEl = btn.child("button:first");
26310             btnEl.on('focus', this.onFocus, this);
26311             btnEl.on('blur', this.onBlur, this);
26312             if(this.cls){
26313                 btn.addClass(this.cls);
26314             }
26315             if(this.icon){
26316                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26317             }
26318             if(this.iconCls){
26319                 btnEl.addClass(this.iconCls);
26320                 if(!this.cls){
26321                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26322                 }
26323             }
26324             if(this.tabIndex !== undefined){
26325                 btnEl.dom.tabIndex = this.tabIndex;
26326             }
26327             if(this.tooltip){
26328                 if(typeof this.tooltip == 'object'){
26329                     Roo.QuickTips.tips(Roo.apply({
26330                           target: btnEl.id
26331                     }, this.tooltip));
26332                 } else {
26333                     btnEl.dom[this.tooltipType] = this.tooltip;
26334                 }
26335             }
26336         }else{
26337             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26338         }
26339         this.el = btn;
26340         if(this.id){
26341             this.el.dom.id = this.el.id = this.id;
26342         }
26343         if(this.menu){
26344             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26345             this.menu.on("show", this.onMenuShow, this);
26346             this.menu.on("hide", this.onMenuHide, this);
26347         }
26348         btn.addClass("x-btn");
26349         if(Roo.isIE && !Roo.isIE7){
26350             this.autoWidth.defer(1, this);
26351         }else{
26352             this.autoWidth();
26353         }
26354         if(this.handleMouseEvents){
26355             btn.on("mouseover", this.onMouseOver, this);
26356             btn.on("mouseout", this.onMouseOut, this);
26357             btn.on("mousedown", this.onMouseDown, this);
26358         }
26359         btn.on(this.clickEvent, this.onClick, this);
26360         //btn.on("mouseup", this.onMouseUp, this);
26361         if(this.hidden){
26362             this.hide();
26363         }
26364         if(this.disabled){
26365             this.disable();
26366         }
26367         Roo.ButtonToggleMgr.register(this);
26368         if(this.pressed){
26369             this.el.addClass("x-btn-pressed");
26370         }
26371         if(this.repeat){
26372             var repeater = new Roo.util.ClickRepeater(btn,
26373                 typeof this.repeat == "object" ? this.repeat : {}
26374             );
26375             repeater.on("click", this.onClick,  this);
26376         }
26377         
26378         this.fireEvent('render', this);
26379         
26380     },
26381     /**
26382      * Returns the button's underlying element
26383      * @return {Roo.Element} The element
26384      */
26385     getEl : function(){
26386         return this.el;  
26387     },
26388     
26389     /**
26390      * Destroys this Button and removes any listeners.
26391      */
26392     destroy : function(){
26393         Roo.ButtonToggleMgr.unregister(this);
26394         this.el.removeAllListeners();
26395         this.purgeListeners();
26396         this.el.remove();
26397     },
26398
26399     // private
26400     autoWidth : function(){
26401         if(this.el){
26402             this.el.setWidth("auto");
26403             if(Roo.isIE7 && Roo.isStrict){
26404                 var ib = this.el.child('button');
26405                 if(ib && ib.getWidth() > 20){
26406                     ib.clip();
26407                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26408                 }
26409             }
26410             if(this.minWidth){
26411                 if(this.hidden){
26412                     this.el.beginMeasure();
26413                 }
26414                 if(this.el.getWidth() < this.minWidth){
26415                     this.el.setWidth(this.minWidth);
26416                 }
26417                 if(this.hidden){
26418                     this.el.endMeasure();
26419                 }
26420             }
26421         }
26422     },
26423
26424     /**
26425      * Assigns this button's click handler
26426      * @param {Function} handler The function to call when the button is clicked
26427      * @param {Object} scope (optional) Scope for the function passed in
26428      */
26429     setHandler : function(handler, scope){
26430         this.handler = handler;
26431         this.scope = scope;  
26432     },
26433     
26434     /**
26435      * Sets this button's text
26436      * @param {String} text The button text
26437      */
26438     setText : function(text){
26439         this.text = text;
26440         if(this.el){
26441             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26442         }
26443         this.autoWidth();
26444     },
26445     
26446     /**
26447      * Gets the text for this button
26448      * @return {String} The button text
26449      */
26450     getText : function(){
26451         return this.text;  
26452     },
26453     
26454     /**
26455      * Show this button
26456      */
26457     show: function(){
26458         this.hidden = false;
26459         if(this.el){
26460             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26461         }
26462     },
26463     
26464     /**
26465      * Hide this button
26466      */
26467     hide: function(){
26468         this.hidden = true;
26469         if(this.el){
26470             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26471         }
26472     },
26473     
26474     /**
26475      * Convenience function for boolean show/hide
26476      * @param {Boolean} visible True to show, false to hide
26477      */
26478     setVisible: function(visible){
26479         if(visible) {
26480             this.show();
26481         }else{
26482             this.hide();
26483         }
26484     },
26485     
26486     /**
26487      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26488      * @param {Boolean} state (optional) Force a particular state
26489      */
26490     toggle : function(state){
26491         state = state === undefined ? !this.pressed : state;
26492         if(state != this.pressed){
26493             if(state){
26494                 this.el.addClass("x-btn-pressed");
26495                 this.pressed = true;
26496                 this.fireEvent("toggle", this, true);
26497             }else{
26498                 this.el.removeClass("x-btn-pressed");
26499                 this.pressed = false;
26500                 this.fireEvent("toggle", this, false);
26501             }
26502             if(this.toggleHandler){
26503                 this.toggleHandler.call(this.scope || this, this, state);
26504             }
26505         }
26506     },
26507     
26508     /**
26509      * Focus the button
26510      */
26511     focus : function(){
26512         this.el.child('button:first').focus();
26513     },
26514     
26515     /**
26516      * Disable this button
26517      */
26518     disable : function(){
26519         if(this.el){
26520             this.el.addClass("x-btn-disabled");
26521         }
26522         this.disabled = true;
26523     },
26524     
26525     /**
26526      * Enable this button
26527      */
26528     enable : function(){
26529         if(this.el){
26530             this.el.removeClass("x-btn-disabled");
26531         }
26532         this.disabled = false;
26533     },
26534
26535     /**
26536      * Convenience function for boolean enable/disable
26537      * @param {Boolean} enabled True to enable, false to disable
26538      */
26539     setDisabled : function(v){
26540         this[v !== true ? "enable" : "disable"]();
26541     },
26542
26543     // private
26544     onClick : function(e){
26545         if(e){
26546             e.preventDefault();
26547         }
26548         if(e.button != 0){
26549             return;
26550         }
26551         if(!this.disabled){
26552             if(this.enableToggle){
26553                 this.toggle();
26554             }
26555             if(this.menu && !this.menu.isVisible()){
26556                 this.menu.show(this.el, this.menuAlign);
26557             }
26558             this.fireEvent("click", this, e);
26559             if(this.handler){
26560                 this.el.removeClass("x-btn-over");
26561                 this.handler.call(this.scope || this, this, e);
26562             }
26563         }
26564     },
26565     // private
26566     onMouseOver : function(e){
26567         if(!this.disabled){
26568             this.el.addClass("x-btn-over");
26569             this.fireEvent('mouseover', this, e);
26570         }
26571     },
26572     // private
26573     onMouseOut : function(e){
26574         if(!e.within(this.el,  true)){
26575             this.el.removeClass("x-btn-over");
26576             this.fireEvent('mouseout', this, e);
26577         }
26578     },
26579     // private
26580     onFocus : function(e){
26581         if(!this.disabled){
26582             this.el.addClass("x-btn-focus");
26583         }
26584     },
26585     // private
26586     onBlur : function(e){
26587         this.el.removeClass("x-btn-focus");
26588     },
26589     // private
26590     onMouseDown : function(e){
26591         if(!this.disabled && e.button == 0){
26592             this.el.addClass("x-btn-click");
26593             Roo.get(document).on('mouseup', this.onMouseUp, this);
26594         }
26595     },
26596     // private
26597     onMouseUp : function(e){
26598         if(e.button == 0){
26599             this.el.removeClass("x-btn-click");
26600             Roo.get(document).un('mouseup', this.onMouseUp, this);
26601         }
26602     },
26603     // private
26604     onMenuShow : function(e){
26605         this.el.addClass("x-btn-menu-active");
26606     },
26607     // private
26608     onMenuHide : function(e){
26609         this.el.removeClass("x-btn-menu-active");
26610     }   
26611 });
26612
26613 // Private utility class used by Button
26614 Roo.ButtonToggleMgr = function(){
26615    var groups = {};
26616    
26617    function toggleGroup(btn, state){
26618        if(state){
26619            var g = groups[btn.toggleGroup];
26620            for(var i = 0, l = g.length; i < l; i++){
26621                if(g[i] != btn){
26622                    g[i].toggle(false);
26623                }
26624            }
26625        }
26626    }
26627    
26628    return {
26629        register : function(btn){
26630            if(!btn.toggleGroup){
26631                return;
26632            }
26633            var g = groups[btn.toggleGroup];
26634            if(!g){
26635                g = groups[btn.toggleGroup] = [];
26636            }
26637            g.push(btn);
26638            btn.on("toggle", toggleGroup);
26639        },
26640        
26641        unregister : function(btn){
26642            if(!btn.toggleGroup){
26643                return;
26644            }
26645            var g = groups[btn.toggleGroup];
26646            if(g){
26647                g.remove(btn);
26648                btn.un("toggle", toggleGroup);
26649            }
26650        }
26651    };
26652 }();/*
26653  * Based on:
26654  * Ext JS Library 1.1.1
26655  * Copyright(c) 2006-2007, Ext JS, LLC.
26656  *
26657  * Originally Released Under LGPL - original licence link has changed is not relivant.
26658  *
26659  * Fork - LGPL
26660  * <script type="text/javascript">
26661  */
26662  
26663 /**
26664  * @class Roo.SplitButton
26665  * @extends Roo.Button
26666  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26667  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26668  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26669  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26670  * @cfg {String} arrowTooltip The title attribute of the arrow
26671  * @constructor
26672  * Create a new menu button
26673  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26674  * @param {Object} config The config object
26675  */
26676 Roo.SplitButton = function(renderTo, config){
26677     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26678     /**
26679      * @event arrowclick
26680      * Fires when this button's arrow is clicked
26681      * @param {SplitButton} this
26682      * @param {EventObject} e The click event
26683      */
26684     this.addEvents({"arrowclick":true});
26685 };
26686
26687 Roo.extend(Roo.SplitButton, Roo.Button, {
26688     render : function(renderTo){
26689         // this is one sweet looking template!
26690         var tpl = new Roo.Template(
26691             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26692             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26693             '<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>',
26694             "</tbody></table></td><td>",
26695             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26696             '<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>',
26697             "</tbody></table></td></tr></table>"
26698         );
26699         var btn = tpl.append(renderTo, [this.text, this.type], true);
26700         var btnEl = btn.child("button");
26701         if(this.cls){
26702             btn.addClass(this.cls);
26703         }
26704         if(this.icon){
26705             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26706         }
26707         if(this.iconCls){
26708             btnEl.addClass(this.iconCls);
26709             if(!this.cls){
26710                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26711             }
26712         }
26713         this.el = btn;
26714         if(this.handleMouseEvents){
26715             btn.on("mouseover", this.onMouseOver, this);
26716             btn.on("mouseout", this.onMouseOut, this);
26717             btn.on("mousedown", this.onMouseDown, this);
26718             btn.on("mouseup", this.onMouseUp, this);
26719         }
26720         btn.on(this.clickEvent, this.onClick, this);
26721         if(this.tooltip){
26722             if(typeof this.tooltip == 'object'){
26723                 Roo.QuickTips.tips(Roo.apply({
26724                       target: btnEl.id
26725                 }, this.tooltip));
26726             } else {
26727                 btnEl.dom[this.tooltipType] = this.tooltip;
26728             }
26729         }
26730         if(this.arrowTooltip){
26731             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26732         }
26733         if(this.hidden){
26734             this.hide();
26735         }
26736         if(this.disabled){
26737             this.disable();
26738         }
26739         if(this.pressed){
26740             this.el.addClass("x-btn-pressed");
26741         }
26742         if(Roo.isIE && !Roo.isIE7){
26743             this.autoWidth.defer(1, this);
26744         }else{
26745             this.autoWidth();
26746         }
26747         if(this.menu){
26748             this.menu.on("show", this.onMenuShow, this);
26749             this.menu.on("hide", this.onMenuHide, this);
26750         }
26751         this.fireEvent('render', this);
26752     },
26753
26754     // private
26755     autoWidth : function(){
26756         if(this.el){
26757             var tbl = this.el.child("table:first");
26758             var tbl2 = this.el.child("table:last");
26759             this.el.setWidth("auto");
26760             tbl.setWidth("auto");
26761             if(Roo.isIE7 && Roo.isStrict){
26762                 var ib = this.el.child('button:first');
26763                 if(ib && ib.getWidth() > 20){
26764                     ib.clip();
26765                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26766                 }
26767             }
26768             if(this.minWidth){
26769                 if(this.hidden){
26770                     this.el.beginMeasure();
26771                 }
26772                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26773                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26774                 }
26775                 if(this.hidden){
26776                     this.el.endMeasure();
26777                 }
26778             }
26779             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26780         } 
26781     },
26782     /**
26783      * Sets this button's click handler
26784      * @param {Function} handler The function to call when the button is clicked
26785      * @param {Object} scope (optional) Scope for the function passed above
26786      */
26787     setHandler : function(handler, scope){
26788         this.handler = handler;
26789         this.scope = scope;  
26790     },
26791     
26792     /**
26793      * Sets this button's arrow click handler
26794      * @param {Function} handler The function to call when the arrow is clicked
26795      * @param {Object} scope (optional) Scope for the function passed above
26796      */
26797     setArrowHandler : function(handler, scope){
26798         this.arrowHandler = handler;
26799         this.scope = scope;  
26800     },
26801     
26802     /**
26803      * Focus the button
26804      */
26805     focus : function(){
26806         if(this.el){
26807             this.el.child("button:first").focus();
26808         }
26809     },
26810
26811     // private
26812     onClick : function(e){
26813         e.preventDefault();
26814         if(!this.disabled){
26815             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26816                 if(this.menu && !this.menu.isVisible()){
26817                     this.menu.show(this.el, this.menuAlign);
26818                 }
26819                 this.fireEvent("arrowclick", this, e);
26820                 if(this.arrowHandler){
26821                     this.arrowHandler.call(this.scope || this, this, e);
26822                 }
26823             }else{
26824                 this.fireEvent("click", this, e);
26825                 if(this.handler){
26826                     this.handler.call(this.scope || this, this, e);
26827                 }
26828             }
26829         }
26830     },
26831     // private
26832     onMouseDown : function(e){
26833         if(!this.disabled){
26834             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26835         }
26836     },
26837     // private
26838     onMouseUp : function(e){
26839         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26840     }   
26841 });
26842
26843
26844 // backwards compat
26845 Roo.MenuButton = Roo.SplitButton;/*
26846  * Based on:
26847  * Ext JS Library 1.1.1
26848  * Copyright(c) 2006-2007, Ext JS, LLC.
26849  *
26850  * Originally Released Under LGPL - original licence link has changed is not relivant.
26851  *
26852  * Fork - LGPL
26853  * <script type="text/javascript">
26854  */
26855
26856 /**
26857  * @class Roo.Toolbar
26858  * Basic Toolbar class.
26859  * @constructor
26860  * Creates a new Toolbar
26861  * @param {Object} container The config object
26862  */ 
26863 Roo.Toolbar = function(container, buttons, config)
26864 {
26865     /// old consturctor format still supported..
26866     if(container instanceof Array){ // omit the container for later rendering
26867         buttons = container;
26868         config = buttons;
26869         container = null;
26870     }
26871     if (typeof(container) == 'object' && container.xtype) {
26872         config = container;
26873         container = config.container;
26874         buttons = config.buttons || []; // not really - use items!!
26875     }
26876     var xitems = [];
26877     if (config && config.items) {
26878         xitems = config.items;
26879         delete config.items;
26880     }
26881     Roo.apply(this, config);
26882     this.buttons = buttons;
26883     
26884     if(container){
26885         this.render(container);
26886     }
26887     this.xitems = xitems;
26888     Roo.each(xitems, function(b) {
26889         this.add(b);
26890     }, this);
26891     
26892 };
26893
26894 Roo.Toolbar.prototype = {
26895     /**
26896      * @cfg {Array} items
26897      * array of button configs or elements to add (will be converted to a MixedCollection)
26898      */
26899     
26900     /**
26901      * @cfg {String/HTMLElement/Element} container
26902      * The id or element that will contain the toolbar
26903      */
26904     // private
26905     render : function(ct){
26906         this.el = Roo.get(ct);
26907         if(this.cls){
26908             this.el.addClass(this.cls);
26909         }
26910         // using a table allows for vertical alignment
26911         // 100% width is needed by Safari...
26912         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26913         this.tr = this.el.child("tr", true);
26914         var autoId = 0;
26915         this.items = new Roo.util.MixedCollection(false, function(o){
26916             return o.id || ("item" + (++autoId));
26917         });
26918         if(this.buttons){
26919             this.add.apply(this, this.buttons);
26920             delete this.buttons;
26921         }
26922     },
26923
26924     /**
26925      * Adds element(s) to the toolbar -- this function takes a variable number of 
26926      * arguments of mixed type and adds them to the toolbar.
26927      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26928      * <ul>
26929      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26930      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26931      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26932      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26933      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26934      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26935      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26936      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26937      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26938      * </ul>
26939      * @param {Mixed} arg2
26940      * @param {Mixed} etc.
26941      */
26942     add : function(){
26943         var a = arguments, l = a.length;
26944         for(var i = 0; i < l; i++){
26945             this._add(a[i]);
26946         }
26947     },
26948     // private..
26949     _add : function(el) {
26950         
26951         if (el.xtype) {
26952             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26953         }
26954         
26955         if (el.applyTo){ // some kind of form field
26956             return this.addField(el);
26957         } 
26958         if (el.render){ // some kind of Toolbar.Item
26959             return this.addItem(el);
26960         }
26961         if (typeof el == "string"){ // string
26962             if(el == "separator" || el == "-"){
26963                 return this.addSeparator();
26964             }
26965             if (el == " "){
26966                 return this.addSpacer();
26967             }
26968             if(el == "->"){
26969                 return this.addFill();
26970             }
26971             return this.addText(el);
26972             
26973         }
26974         if(el.tagName){ // element
26975             return this.addElement(el);
26976         }
26977         if(typeof el == "object"){ // must be button config?
26978             return this.addButton(el);
26979         }
26980         // and now what?!?!
26981         return false;
26982         
26983     },
26984     
26985     /**
26986      * Add an Xtype element
26987      * @param {Object} xtype Xtype Object
26988      * @return {Object} created Object
26989      */
26990     addxtype : function(e){
26991         return this.add(e);  
26992     },
26993     
26994     /**
26995      * Returns the Element for this toolbar.
26996      * @return {Roo.Element}
26997      */
26998     getEl : function(){
26999         return this.el;  
27000     },
27001     
27002     /**
27003      * Adds a separator
27004      * @return {Roo.Toolbar.Item} The separator item
27005      */
27006     addSeparator : function(){
27007         return this.addItem(new Roo.Toolbar.Separator());
27008     },
27009
27010     /**
27011      * Adds a spacer element
27012      * @return {Roo.Toolbar.Spacer} The spacer item
27013      */
27014     addSpacer : function(){
27015         return this.addItem(new Roo.Toolbar.Spacer());
27016     },
27017
27018     /**
27019      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27020      * @return {Roo.Toolbar.Fill} The fill item
27021      */
27022     addFill : function(){
27023         return this.addItem(new Roo.Toolbar.Fill());
27024     },
27025
27026     /**
27027      * Adds any standard HTML element to the toolbar
27028      * @param {String/HTMLElement/Element} el The element or id of the element to add
27029      * @return {Roo.Toolbar.Item} The element's item
27030      */
27031     addElement : function(el){
27032         return this.addItem(new Roo.Toolbar.Item(el));
27033     },
27034     /**
27035      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27036      * @type Roo.util.MixedCollection  
27037      */
27038     items : false,
27039      
27040     /**
27041      * Adds any Toolbar.Item or subclass
27042      * @param {Roo.Toolbar.Item} item
27043      * @return {Roo.Toolbar.Item} The item
27044      */
27045     addItem : function(item){
27046         var td = this.nextBlock();
27047         item.render(td);
27048         this.items.add(item);
27049         return item;
27050     },
27051     
27052     /**
27053      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27054      * @param {Object/Array} config A button config or array of configs
27055      * @return {Roo.Toolbar.Button/Array}
27056      */
27057     addButton : function(config){
27058         if(config instanceof Array){
27059             var buttons = [];
27060             for(var i = 0, len = config.length; i < len; i++) {
27061                 buttons.push(this.addButton(config[i]));
27062             }
27063             return buttons;
27064         }
27065         var b = config;
27066         if(!(config instanceof Roo.Toolbar.Button)){
27067             b = config.split ?
27068                 new Roo.Toolbar.SplitButton(config) :
27069                 new Roo.Toolbar.Button(config);
27070         }
27071         var td = this.nextBlock();
27072         b.render(td);
27073         this.items.add(b);
27074         return b;
27075     },
27076     
27077     /**
27078      * Adds text to the toolbar
27079      * @param {String} text The text to add
27080      * @return {Roo.Toolbar.Item} The element's item
27081      */
27082     addText : function(text){
27083         return this.addItem(new Roo.Toolbar.TextItem(text));
27084     },
27085     
27086     /**
27087      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27088      * @param {Number} index The index where the item is to be inserted
27089      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27090      * @return {Roo.Toolbar.Button/Item}
27091      */
27092     insertButton : function(index, item){
27093         if(item instanceof Array){
27094             var buttons = [];
27095             for(var i = 0, len = item.length; i < len; i++) {
27096                buttons.push(this.insertButton(index + i, item[i]));
27097             }
27098             return buttons;
27099         }
27100         if (!(item instanceof Roo.Toolbar.Button)){
27101            item = new Roo.Toolbar.Button(item);
27102         }
27103         var td = document.createElement("td");
27104         this.tr.insertBefore(td, this.tr.childNodes[index]);
27105         item.render(td);
27106         this.items.insert(index, item);
27107         return item;
27108     },
27109     
27110     /**
27111      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27112      * @param {Object} config
27113      * @return {Roo.Toolbar.Item} The element's item
27114      */
27115     addDom : function(config, returnEl){
27116         var td = this.nextBlock();
27117         Roo.DomHelper.overwrite(td, config);
27118         var ti = new Roo.Toolbar.Item(td.firstChild);
27119         ti.render(td);
27120         this.items.add(ti);
27121         return ti;
27122     },
27123
27124     /**
27125      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27126      * @type Roo.util.MixedCollection  
27127      */
27128     fields : false,
27129     
27130     /**
27131      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27132      * Note: the field should not have been rendered yet. For a field that has already been
27133      * rendered, use {@link #addElement}.
27134      * @param {Roo.form.Field} field
27135      * @return {Roo.ToolbarItem}
27136      */
27137      
27138       
27139     addField : function(field) {
27140         if (!this.fields) {
27141             var autoId = 0;
27142             this.fields = new Roo.util.MixedCollection(false, function(o){
27143                 return o.id || ("item" + (++autoId));
27144             });
27145
27146         }
27147         
27148         var td = this.nextBlock();
27149         field.render(td);
27150         var ti = new Roo.Toolbar.Item(td.firstChild);
27151         ti.render(td);
27152         this.items.add(ti);
27153         this.fields.add(field);
27154         return ti;
27155     },
27156     /**
27157      * Hide the toolbar
27158      * @method hide
27159      */
27160      
27161       
27162     hide : function()
27163     {
27164         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27165         this.el.child('div').hide();
27166     },
27167     /**
27168      * Show the toolbar
27169      * @method show
27170      */
27171     show : function()
27172     {
27173         this.el.child('div').show();
27174     },
27175       
27176     // private
27177     nextBlock : function(){
27178         var td = document.createElement("td");
27179         this.tr.appendChild(td);
27180         return td;
27181     },
27182
27183     // private
27184     destroy : function(){
27185         if(this.items){ // rendered?
27186             Roo.destroy.apply(Roo, this.items.items);
27187         }
27188         if(this.fields){ // rendered?
27189             Roo.destroy.apply(Roo, this.fields.items);
27190         }
27191         Roo.Element.uncache(this.el, this.tr);
27192     }
27193 };
27194
27195 /**
27196  * @class Roo.Toolbar.Item
27197  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27198  * @constructor
27199  * Creates a new Item
27200  * @param {HTMLElement} el 
27201  */
27202 Roo.Toolbar.Item = function(el){
27203     this.el = Roo.getDom(el);
27204     this.id = Roo.id(this.el);
27205     this.hidden = false;
27206 };
27207
27208 Roo.Toolbar.Item.prototype = {
27209     
27210     /**
27211      * Get this item's HTML Element
27212      * @return {HTMLElement}
27213      */
27214     getEl : function(){
27215        return this.el;  
27216     },
27217
27218     // private
27219     render : function(td){
27220         this.td = td;
27221         td.appendChild(this.el);
27222     },
27223     
27224     /**
27225      * Removes and destroys this item.
27226      */
27227     destroy : function(){
27228         this.td.parentNode.removeChild(this.td);
27229     },
27230     
27231     /**
27232      * Shows this item.
27233      */
27234     show: function(){
27235         this.hidden = false;
27236         this.td.style.display = "";
27237     },
27238     
27239     /**
27240      * Hides this item.
27241      */
27242     hide: function(){
27243         this.hidden = true;
27244         this.td.style.display = "none";
27245     },
27246     
27247     /**
27248      * Convenience function for boolean show/hide.
27249      * @param {Boolean} visible true to show/false to hide
27250      */
27251     setVisible: function(visible){
27252         if(visible) {
27253             this.show();
27254         }else{
27255             this.hide();
27256         }
27257     },
27258     
27259     /**
27260      * Try to focus this item.
27261      */
27262     focus : function(){
27263         Roo.fly(this.el).focus();
27264     },
27265     
27266     /**
27267      * Disables this item.
27268      */
27269     disable : function(){
27270         Roo.fly(this.td).addClass("x-item-disabled");
27271         this.disabled = true;
27272         this.el.disabled = true;
27273     },
27274     
27275     /**
27276      * Enables this item.
27277      */
27278     enable : function(){
27279         Roo.fly(this.td).removeClass("x-item-disabled");
27280         this.disabled = false;
27281         this.el.disabled = false;
27282     }
27283 };
27284
27285
27286 /**
27287  * @class Roo.Toolbar.Separator
27288  * @extends Roo.Toolbar.Item
27289  * A simple toolbar separator class
27290  * @constructor
27291  * Creates a new Separator
27292  */
27293 Roo.Toolbar.Separator = function(){
27294     var s = document.createElement("span");
27295     s.className = "ytb-sep";
27296     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27297 };
27298 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27299     enable:Roo.emptyFn,
27300     disable:Roo.emptyFn,
27301     focus:Roo.emptyFn
27302 });
27303
27304 /**
27305  * @class Roo.Toolbar.Spacer
27306  * @extends Roo.Toolbar.Item
27307  * A simple element that adds extra horizontal space to a toolbar.
27308  * @constructor
27309  * Creates a new Spacer
27310  */
27311 Roo.Toolbar.Spacer = function(){
27312     var s = document.createElement("div");
27313     s.className = "ytb-spacer";
27314     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27315 };
27316 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27317     enable:Roo.emptyFn,
27318     disable:Roo.emptyFn,
27319     focus:Roo.emptyFn
27320 });
27321
27322 /**
27323  * @class Roo.Toolbar.Fill
27324  * @extends Roo.Toolbar.Spacer
27325  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27326  * @constructor
27327  * Creates a new Spacer
27328  */
27329 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27330     // private
27331     render : function(td){
27332         td.style.width = '100%';
27333         Roo.Toolbar.Fill.superclass.render.call(this, td);
27334     }
27335 });
27336
27337 /**
27338  * @class Roo.Toolbar.TextItem
27339  * @extends Roo.Toolbar.Item
27340  * A simple class that renders text directly into a toolbar.
27341  * @constructor
27342  * Creates a new TextItem
27343  * @param {String} text
27344  */
27345 Roo.Toolbar.TextItem = function(text){
27346     if (typeof(text) == 'object') {
27347         text = text.text;
27348     }
27349     var s = document.createElement("span");
27350     s.className = "ytb-text";
27351     s.innerHTML = text;
27352     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27353 };
27354 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27355     enable:Roo.emptyFn,
27356     disable:Roo.emptyFn,
27357     focus:Roo.emptyFn
27358 });
27359
27360 /**
27361  * @class Roo.Toolbar.Button
27362  * @extends Roo.Button
27363  * A button that renders into a toolbar.
27364  * @constructor
27365  * Creates a new Button
27366  * @param {Object} config A standard {@link Roo.Button} config object
27367  */
27368 Roo.Toolbar.Button = function(config){
27369     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27370 };
27371 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27372     render : function(td){
27373         this.td = td;
27374         Roo.Toolbar.Button.superclass.render.call(this, td);
27375     },
27376     
27377     /**
27378      * Removes and destroys this button
27379      */
27380     destroy : function(){
27381         Roo.Toolbar.Button.superclass.destroy.call(this);
27382         this.td.parentNode.removeChild(this.td);
27383     },
27384     
27385     /**
27386      * Shows this button
27387      */
27388     show: function(){
27389         this.hidden = false;
27390         this.td.style.display = "";
27391     },
27392     
27393     /**
27394      * Hides this button
27395      */
27396     hide: function(){
27397         this.hidden = true;
27398         this.td.style.display = "none";
27399     },
27400
27401     /**
27402      * Disables this item
27403      */
27404     disable : function(){
27405         Roo.fly(this.td).addClass("x-item-disabled");
27406         this.disabled = true;
27407     },
27408
27409     /**
27410      * Enables this item
27411      */
27412     enable : function(){
27413         Roo.fly(this.td).removeClass("x-item-disabled");
27414         this.disabled = false;
27415     }
27416 });
27417 // backwards compat
27418 Roo.ToolbarButton = Roo.Toolbar.Button;
27419
27420 /**
27421  * @class Roo.Toolbar.SplitButton
27422  * @extends Roo.SplitButton
27423  * A menu button that renders into a toolbar.
27424  * @constructor
27425  * Creates a new SplitButton
27426  * @param {Object} config A standard {@link Roo.SplitButton} config object
27427  */
27428 Roo.Toolbar.SplitButton = function(config){
27429     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27430 };
27431 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27432     render : function(td){
27433         this.td = td;
27434         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27435     },
27436     
27437     /**
27438      * Removes and destroys this button
27439      */
27440     destroy : function(){
27441         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27442         this.td.parentNode.removeChild(this.td);
27443     },
27444     
27445     /**
27446      * Shows this button
27447      */
27448     show: function(){
27449         this.hidden = false;
27450         this.td.style.display = "";
27451     },
27452     
27453     /**
27454      * Hides this button
27455      */
27456     hide: function(){
27457         this.hidden = true;
27458         this.td.style.display = "none";
27459     }
27460 });
27461
27462 // backwards compat
27463 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27464  * Based on:
27465  * Ext JS Library 1.1.1
27466  * Copyright(c) 2006-2007, Ext JS, LLC.
27467  *
27468  * Originally Released Under LGPL - original licence link has changed is not relivant.
27469  *
27470  * Fork - LGPL
27471  * <script type="text/javascript">
27472  */
27473  
27474 /**
27475  * @class Roo.PagingToolbar
27476  * @extends Roo.Toolbar
27477  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27478  * @constructor
27479  * Create a new PagingToolbar
27480  * @param {Object} config The config object
27481  */
27482 Roo.PagingToolbar = function(el, ds, config)
27483 {
27484     // old args format still supported... - xtype is prefered..
27485     if (typeof(el) == 'object' && el.xtype) {
27486         // created from xtype...
27487         config = el;
27488         ds = el.dataSource;
27489         el = config.container;
27490     }
27491     var items = [];
27492     if (config.items) {
27493         items = config.items;
27494         config.items = [];
27495     }
27496     
27497     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27498     this.ds = ds;
27499     this.cursor = 0;
27500     this.renderButtons(this.el);
27501     this.bind(ds);
27502     
27503     // supprot items array.
27504    
27505     Roo.each(items, function(e) {
27506         this.add(Roo.factory(e));
27507     },this);
27508     
27509 };
27510
27511 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27512     /**
27513      * @cfg {Roo.data.Store} dataSource
27514      * The underlying data store providing the paged data
27515      */
27516     /**
27517      * @cfg {String/HTMLElement/Element} container
27518      * container The id or element that will contain the toolbar
27519      */
27520     /**
27521      * @cfg {Boolean} displayInfo
27522      * True to display the displayMsg (defaults to false)
27523      */
27524     /**
27525      * @cfg {Number} pageSize
27526      * The number of records to display per page (defaults to 20)
27527      */
27528     pageSize: 20,
27529     /**
27530      * @cfg {String} displayMsg
27531      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27532      */
27533     displayMsg : 'Displaying {0} - {1} of {2}',
27534     /**
27535      * @cfg {String} emptyMsg
27536      * The message to display when no records are found (defaults to "No data to display")
27537      */
27538     emptyMsg : 'No data to display',
27539     /**
27540      * Customizable piece of the default paging text (defaults to "Page")
27541      * @type String
27542      */
27543     beforePageText : "Page",
27544     /**
27545      * Customizable piece of the default paging text (defaults to "of %0")
27546      * @type String
27547      */
27548     afterPageText : "of {0}",
27549     /**
27550      * Customizable piece of the default paging text (defaults to "First Page")
27551      * @type String
27552      */
27553     firstText : "First Page",
27554     /**
27555      * Customizable piece of the default paging text (defaults to "Previous Page")
27556      * @type String
27557      */
27558     prevText : "Previous Page",
27559     /**
27560      * Customizable piece of the default paging text (defaults to "Next Page")
27561      * @type String
27562      */
27563     nextText : "Next Page",
27564     /**
27565      * Customizable piece of the default paging text (defaults to "Last Page")
27566      * @type String
27567      */
27568     lastText : "Last Page",
27569     /**
27570      * Customizable piece of the default paging text (defaults to "Refresh")
27571      * @type String
27572      */
27573     refreshText : "Refresh",
27574
27575     // private
27576     renderButtons : function(el){
27577         Roo.PagingToolbar.superclass.render.call(this, el);
27578         this.first = this.addButton({
27579             tooltip: this.firstText,
27580             cls: "x-btn-icon x-grid-page-first",
27581             disabled: true,
27582             handler: this.onClick.createDelegate(this, ["first"])
27583         });
27584         this.prev = this.addButton({
27585             tooltip: this.prevText,
27586             cls: "x-btn-icon x-grid-page-prev",
27587             disabled: true,
27588             handler: this.onClick.createDelegate(this, ["prev"])
27589         });
27590         //this.addSeparator();
27591         this.add(this.beforePageText);
27592         this.field = Roo.get(this.addDom({
27593            tag: "input",
27594            type: "text",
27595            size: "3",
27596            value: "1",
27597            cls: "x-grid-page-number"
27598         }).el);
27599         this.field.on("keydown", this.onPagingKeydown, this);
27600         this.field.on("focus", function(){this.dom.select();});
27601         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27602         this.field.setHeight(18);
27603         //this.addSeparator();
27604         this.next = this.addButton({
27605             tooltip: this.nextText,
27606             cls: "x-btn-icon x-grid-page-next",
27607             disabled: true,
27608             handler: this.onClick.createDelegate(this, ["next"])
27609         });
27610         this.last = this.addButton({
27611             tooltip: this.lastText,
27612             cls: "x-btn-icon x-grid-page-last",
27613             disabled: true,
27614             handler: this.onClick.createDelegate(this, ["last"])
27615         });
27616         //this.addSeparator();
27617         this.loading = this.addButton({
27618             tooltip: this.refreshText,
27619             cls: "x-btn-icon x-grid-loading",
27620             handler: this.onClick.createDelegate(this, ["refresh"])
27621         });
27622
27623         if(this.displayInfo){
27624             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27625         }
27626     },
27627
27628     // private
27629     updateInfo : function(){
27630         if(this.displayEl){
27631             var count = this.ds.getCount();
27632             var msg = count == 0 ?
27633                 this.emptyMsg :
27634                 String.format(
27635                     this.displayMsg,
27636                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27637                 );
27638             this.displayEl.update(msg);
27639         }
27640     },
27641
27642     // private
27643     onLoad : function(ds, r, o){
27644        this.cursor = o.params ? o.params.start : 0;
27645        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27646
27647        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27648        this.field.dom.value = ap;
27649        this.first.setDisabled(ap == 1);
27650        this.prev.setDisabled(ap == 1);
27651        this.next.setDisabled(ap == ps);
27652        this.last.setDisabled(ap == ps);
27653        this.loading.enable();
27654        this.updateInfo();
27655     },
27656
27657     // private
27658     getPageData : function(){
27659         var total = this.ds.getTotalCount();
27660         return {
27661             total : total,
27662             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27663             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27664         };
27665     },
27666
27667     // private
27668     onLoadError : function(){
27669         this.loading.enable();
27670     },
27671
27672     // private
27673     onPagingKeydown : function(e){
27674         var k = e.getKey();
27675         var d = this.getPageData();
27676         if(k == e.RETURN){
27677             var v = this.field.dom.value, pageNum;
27678             if(!v || isNaN(pageNum = parseInt(v, 10))){
27679                 this.field.dom.value = d.activePage;
27680                 return;
27681             }
27682             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27683             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27684             e.stopEvent();
27685         }
27686         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))
27687         {
27688           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27689           this.field.dom.value = pageNum;
27690           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27691           e.stopEvent();
27692         }
27693         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27694         {
27695           var v = this.field.dom.value, pageNum; 
27696           var increment = (e.shiftKey) ? 10 : 1;
27697           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27698             increment *= -1;
27699           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27700             this.field.dom.value = d.activePage;
27701             return;
27702           }
27703           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27704           {
27705             this.field.dom.value = parseInt(v, 10) + increment;
27706             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27707             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27708           }
27709           e.stopEvent();
27710         }
27711     },
27712
27713     // private
27714     beforeLoad : function(){
27715         if(this.loading){
27716             this.loading.disable();
27717         }
27718     },
27719
27720     // private
27721     onClick : function(which){
27722         var ds = this.ds;
27723         switch(which){
27724             case "first":
27725                 ds.load({params:{start: 0, limit: this.pageSize}});
27726             break;
27727             case "prev":
27728                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27729             break;
27730             case "next":
27731                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27732             break;
27733             case "last":
27734                 var total = ds.getTotalCount();
27735                 var extra = total % this.pageSize;
27736                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27737                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27738             break;
27739             case "refresh":
27740                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27741             break;
27742         }
27743     },
27744
27745     /**
27746      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27747      * @param {Roo.data.Store} store The data store to unbind
27748      */
27749     unbind : function(ds){
27750         ds.un("beforeload", this.beforeLoad, this);
27751         ds.un("load", this.onLoad, this);
27752         ds.un("loadexception", this.onLoadError, this);
27753         ds.un("remove", this.updateInfo, this);
27754         ds.un("add", this.updateInfo, this);
27755         this.ds = undefined;
27756     },
27757
27758     /**
27759      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27760      * @param {Roo.data.Store} store The data store to bind
27761      */
27762     bind : function(ds){
27763         ds.on("beforeload", this.beforeLoad, this);
27764         ds.on("load", this.onLoad, this);
27765         ds.on("loadexception", this.onLoadError, this);
27766         ds.on("remove", this.updateInfo, this);
27767         ds.on("add", this.updateInfo, this);
27768         this.ds = ds;
27769     }
27770 });/*
27771  * Based on:
27772  * Ext JS Library 1.1.1
27773  * Copyright(c) 2006-2007, Ext JS, LLC.
27774  *
27775  * Originally Released Under LGPL - original licence link has changed is not relivant.
27776  *
27777  * Fork - LGPL
27778  * <script type="text/javascript">
27779  */
27780
27781 /**
27782  * @class Roo.Resizable
27783  * @extends Roo.util.Observable
27784  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27785  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27786  * 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
27787  * the element will be wrapped for you automatically.</p>
27788  * <p>Here is the list of valid resize handles:</p>
27789  * <pre>
27790 Value   Description
27791 ------  -------------------
27792  'n'     north
27793  's'     south
27794  'e'     east
27795  'w'     west
27796  'nw'    northwest
27797  'sw'    southwest
27798  'se'    southeast
27799  'ne'    northeast
27800  'hd'    horizontal drag
27801  'all'   all
27802 </pre>
27803  * <p>Here's an example showing the creation of a typical Resizable:</p>
27804  * <pre><code>
27805 var resizer = new Roo.Resizable("element-id", {
27806     handles: 'all',
27807     minWidth: 200,
27808     minHeight: 100,
27809     maxWidth: 500,
27810     maxHeight: 400,
27811     pinned: true
27812 });
27813 resizer.on("resize", myHandler);
27814 </code></pre>
27815  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27816  * resizer.east.setDisplayed(false);</p>
27817  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27818  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27819  * resize operation's new size (defaults to [0, 0])
27820  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27821  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27822  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27823  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27824  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27825  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27826  * @cfg {Number} width The width of the element in pixels (defaults to null)
27827  * @cfg {Number} height The height of the element in pixels (defaults to null)
27828  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27829  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27830  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27831  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27832  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27833  * in favor of the handles config option (defaults to false)
27834  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27835  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27836  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27837  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27838  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27839  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27840  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27841  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27842  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27843  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27844  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27845  * @constructor
27846  * Create a new resizable component
27847  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27848  * @param {Object} config configuration options
27849   */
27850 Roo.Resizable = function(el, config)
27851 {
27852     this.el = Roo.get(el);
27853
27854     if(config && config.wrap){
27855         config.resizeChild = this.el;
27856         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27857         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27858         this.el.setStyle("overflow", "hidden");
27859         this.el.setPositioning(config.resizeChild.getPositioning());
27860         config.resizeChild.clearPositioning();
27861         if(!config.width || !config.height){
27862             var csize = config.resizeChild.getSize();
27863             this.el.setSize(csize.width, csize.height);
27864         }
27865         if(config.pinned && !config.adjustments){
27866             config.adjustments = "auto";
27867         }
27868     }
27869
27870     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27871     this.proxy.unselectable();
27872     this.proxy.enableDisplayMode('block');
27873
27874     Roo.apply(this, config);
27875
27876     if(this.pinned){
27877         this.disableTrackOver = true;
27878         this.el.addClass("x-resizable-pinned");
27879     }
27880     // if the element isn't positioned, make it relative
27881     var position = this.el.getStyle("position");
27882     if(position != "absolute" && position != "fixed"){
27883         this.el.setStyle("position", "relative");
27884     }
27885     if(!this.handles){ // no handles passed, must be legacy style
27886         this.handles = 's,e,se';
27887         if(this.multiDirectional){
27888             this.handles += ',n,w';
27889         }
27890     }
27891     if(this.handles == "all"){
27892         this.handles = "n s e w ne nw se sw";
27893     }
27894     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27895     var ps = Roo.Resizable.positions;
27896     for(var i = 0, len = hs.length; i < len; i++){
27897         if(hs[i] && ps[hs[i]]){
27898             var pos = ps[hs[i]];
27899             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27900         }
27901     }
27902     // legacy
27903     this.corner = this.southeast;
27904     
27905     // updateBox = the box can move..
27906     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27907         this.updateBox = true;
27908     }
27909
27910     this.activeHandle = null;
27911
27912     if(this.resizeChild){
27913         if(typeof this.resizeChild == "boolean"){
27914             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27915         }else{
27916             this.resizeChild = Roo.get(this.resizeChild, true);
27917         }
27918     }
27919     
27920     if(this.adjustments == "auto"){
27921         var rc = this.resizeChild;
27922         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27923         if(rc && (hw || hn)){
27924             rc.position("relative");
27925             rc.setLeft(hw ? hw.el.getWidth() : 0);
27926             rc.setTop(hn ? hn.el.getHeight() : 0);
27927         }
27928         this.adjustments = [
27929             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27930             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27931         ];
27932     }
27933
27934     if(this.draggable){
27935         this.dd = this.dynamic ?
27936             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27937         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27938     }
27939
27940     // public events
27941     this.addEvents({
27942         /**
27943          * @event beforeresize
27944          * Fired before resize is allowed. Set enabled to false to cancel resize.
27945          * @param {Roo.Resizable} this
27946          * @param {Roo.EventObject} e The mousedown event
27947          */
27948         "beforeresize" : true,
27949         /**
27950          * @event resize
27951          * Fired after a resize.
27952          * @param {Roo.Resizable} this
27953          * @param {Number} width The new width
27954          * @param {Number} height The new height
27955          * @param {Roo.EventObject} e The mouseup event
27956          */
27957         "resize" : true
27958     });
27959
27960     if(this.width !== null && this.height !== null){
27961         this.resizeTo(this.width, this.height);
27962     }else{
27963         this.updateChildSize();
27964     }
27965     if(Roo.isIE){
27966         this.el.dom.style.zoom = 1;
27967     }
27968     Roo.Resizable.superclass.constructor.call(this);
27969 };
27970
27971 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27972         resizeChild : false,
27973         adjustments : [0, 0],
27974         minWidth : 5,
27975         minHeight : 5,
27976         maxWidth : 10000,
27977         maxHeight : 10000,
27978         enabled : true,
27979         animate : false,
27980         duration : .35,
27981         dynamic : false,
27982         handles : false,
27983         multiDirectional : false,
27984         disableTrackOver : false,
27985         easing : 'easeOutStrong',
27986         widthIncrement : 0,
27987         heightIncrement : 0,
27988         pinned : false,
27989         width : null,
27990         height : null,
27991         preserveRatio : false,
27992         transparent: false,
27993         minX: 0,
27994         minY: 0,
27995         draggable: false,
27996
27997         /**
27998          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27999          */
28000         constrainTo: undefined,
28001         /**
28002          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28003          */
28004         resizeRegion: undefined,
28005
28006
28007     /**
28008      * Perform a manual resize
28009      * @param {Number} width
28010      * @param {Number} height
28011      */
28012     resizeTo : function(width, height){
28013         this.el.setSize(width, height);
28014         this.updateChildSize();
28015         this.fireEvent("resize", this, width, height, null);
28016     },
28017
28018     // private
28019     startSizing : function(e, handle){
28020         this.fireEvent("beforeresize", this, e);
28021         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28022
28023             if(!this.overlay){
28024                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28025                 this.overlay.unselectable();
28026                 this.overlay.enableDisplayMode("block");
28027                 this.overlay.on("mousemove", this.onMouseMove, this);
28028                 this.overlay.on("mouseup", this.onMouseUp, this);
28029             }
28030             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28031
28032             this.resizing = true;
28033             this.startBox = this.el.getBox();
28034             this.startPoint = e.getXY();
28035             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28036                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28037
28038             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28039             this.overlay.show();
28040
28041             if(this.constrainTo) {
28042                 var ct = Roo.get(this.constrainTo);
28043                 this.resizeRegion = ct.getRegion().adjust(
28044                     ct.getFrameWidth('t'),
28045                     ct.getFrameWidth('l'),
28046                     -ct.getFrameWidth('b'),
28047                     -ct.getFrameWidth('r')
28048                 );
28049             }
28050
28051             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28052             this.proxy.show();
28053             this.proxy.setBox(this.startBox);
28054             if(!this.dynamic){
28055                 this.proxy.setStyle('visibility', 'visible');
28056             }
28057         }
28058     },
28059
28060     // private
28061     onMouseDown : function(handle, e){
28062         if(this.enabled){
28063             e.stopEvent();
28064             this.activeHandle = handle;
28065             this.startSizing(e, handle);
28066         }
28067     },
28068
28069     // private
28070     onMouseUp : function(e){
28071         var size = this.resizeElement();
28072         this.resizing = false;
28073         this.handleOut();
28074         this.overlay.hide();
28075         this.proxy.hide();
28076         this.fireEvent("resize", this, size.width, size.height, e);
28077     },
28078
28079     // private
28080     updateChildSize : function(){
28081         if(this.resizeChild){
28082             var el = this.el;
28083             var child = this.resizeChild;
28084             var adj = this.adjustments;
28085             if(el.dom.offsetWidth){
28086                 var b = el.getSize(true);
28087                 child.setSize(b.width+adj[0], b.height+adj[1]);
28088             }
28089             // Second call here for IE
28090             // The first call enables instant resizing and
28091             // the second call corrects scroll bars if they
28092             // exist
28093             if(Roo.isIE){
28094                 setTimeout(function(){
28095                     if(el.dom.offsetWidth){
28096                         var b = el.getSize(true);
28097                         child.setSize(b.width+adj[0], b.height+adj[1]);
28098                     }
28099                 }, 10);
28100             }
28101         }
28102     },
28103
28104     // private
28105     snap : function(value, inc, min){
28106         if(!inc || !value) return value;
28107         var newValue = value;
28108         var m = value % inc;
28109         if(m > 0){
28110             if(m > (inc/2)){
28111                 newValue = value + (inc-m);
28112             }else{
28113                 newValue = value - m;
28114             }
28115         }
28116         return Math.max(min, newValue);
28117     },
28118
28119     // private
28120     resizeElement : function(){
28121         var box = this.proxy.getBox();
28122         if(this.updateBox){
28123             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28124         }else{
28125             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28126         }
28127         this.updateChildSize();
28128         if(!this.dynamic){
28129             this.proxy.hide();
28130         }
28131         return box;
28132     },
28133
28134     // private
28135     constrain : function(v, diff, m, mx){
28136         if(v - diff < m){
28137             diff = v - m;
28138         }else if(v - diff > mx){
28139             diff = mx - v;
28140         }
28141         return diff;
28142     },
28143
28144     // private
28145     onMouseMove : function(e){
28146         if(this.enabled){
28147             try{// try catch so if something goes wrong the user doesn't get hung
28148
28149             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28150                 return;
28151             }
28152
28153             //var curXY = this.startPoint;
28154             var curSize = this.curSize || this.startBox;
28155             var x = this.startBox.x, y = this.startBox.y;
28156             var ox = x, oy = y;
28157             var w = curSize.width, h = curSize.height;
28158             var ow = w, oh = h;
28159             var mw = this.minWidth, mh = this.minHeight;
28160             var mxw = this.maxWidth, mxh = this.maxHeight;
28161             var wi = this.widthIncrement;
28162             var hi = this.heightIncrement;
28163
28164             var eventXY = e.getXY();
28165             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28166             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28167
28168             var pos = this.activeHandle.position;
28169
28170             switch(pos){
28171                 case "east":
28172                     w += diffX;
28173                     w = Math.min(Math.max(mw, w), mxw);
28174                     break;
28175              
28176                 case "south":
28177                     h += diffY;
28178                     h = Math.min(Math.max(mh, h), mxh);
28179                     break;
28180                 case "southeast":
28181                     w += diffX;
28182                     h += diffY;
28183                     w = Math.min(Math.max(mw, w), mxw);
28184                     h = Math.min(Math.max(mh, h), mxh);
28185                     break;
28186                 case "north":
28187                     diffY = this.constrain(h, diffY, mh, mxh);
28188                     y += diffY;
28189                     h -= diffY;
28190                     break;
28191                 case "hdrag":
28192                     
28193                     if (wi) {
28194                         var adiffX = Math.abs(diffX);
28195                         var sub = (adiffX % wi); // how much 
28196                         if (sub > (wi/2)) { // far enough to snap
28197                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28198                         } else {
28199                             // remove difference.. 
28200                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28201                         }
28202                     }
28203                     x += diffX;
28204                     x = Math.max(this.minX, x);
28205                     break;
28206                 case "west":
28207                     diffX = this.constrain(w, diffX, mw, mxw);
28208                     x += diffX;
28209                     w -= diffX;
28210                     break;
28211                 case "northeast":
28212                     w += diffX;
28213                     w = Math.min(Math.max(mw, w), mxw);
28214                     diffY = this.constrain(h, diffY, mh, mxh);
28215                     y += diffY;
28216                     h -= diffY;
28217                     break;
28218                 case "northwest":
28219                     diffX = this.constrain(w, diffX, mw, mxw);
28220                     diffY = this.constrain(h, diffY, mh, mxh);
28221                     y += diffY;
28222                     h -= diffY;
28223                     x += diffX;
28224                     w -= diffX;
28225                     break;
28226                case "southwest":
28227                     diffX = this.constrain(w, diffX, mw, mxw);
28228                     h += diffY;
28229                     h = Math.min(Math.max(mh, h), mxh);
28230                     x += diffX;
28231                     w -= diffX;
28232                     break;
28233             }
28234
28235             var sw = this.snap(w, wi, mw);
28236             var sh = this.snap(h, hi, mh);
28237             if(sw != w || sh != h){
28238                 switch(pos){
28239                     case "northeast":
28240                         y -= sh - h;
28241                     break;
28242                     case "north":
28243                         y -= sh - h;
28244                         break;
28245                     case "southwest":
28246                         x -= sw - w;
28247                     break;
28248                     case "west":
28249                         x -= sw - w;
28250                         break;
28251                     case "northwest":
28252                         x -= sw - w;
28253                         y -= sh - h;
28254                     break;
28255                 }
28256                 w = sw;
28257                 h = sh;
28258             }
28259
28260             if(this.preserveRatio){
28261                 switch(pos){
28262                     case "southeast":
28263                     case "east":
28264                         h = oh * (w/ow);
28265                         h = Math.min(Math.max(mh, h), mxh);
28266                         w = ow * (h/oh);
28267                        break;
28268                     case "south":
28269                         w = ow * (h/oh);
28270                         w = Math.min(Math.max(mw, w), mxw);
28271                         h = oh * (w/ow);
28272                         break;
28273                     case "northeast":
28274                         w = ow * (h/oh);
28275                         w = Math.min(Math.max(mw, w), mxw);
28276                         h = oh * (w/ow);
28277                     break;
28278                     case "north":
28279                         var tw = w;
28280                         w = ow * (h/oh);
28281                         w = Math.min(Math.max(mw, w), mxw);
28282                         h = oh * (w/ow);
28283                         x += (tw - w) / 2;
28284                         break;
28285                     case "southwest":
28286                         h = oh * (w/ow);
28287                         h = Math.min(Math.max(mh, h), mxh);
28288                         var tw = w;
28289                         w = ow * (h/oh);
28290                         x += tw - w;
28291                         break;
28292                     case "west":
28293                         var th = h;
28294                         h = oh * (w/ow);
28295                         h = Math.min(Math.max(mh, h), mxh);
28296                         y += (th - h) / 2;
28297                         var tw = w;
28298                         w = ow * (h/oh);
28299                         x += tw - w;
28300                        break;
28301                     case "northwest":
28302                         var tw = w;
28303                         var th = h;
28304                         h = oh * (w/ow);
28305                         h = Math.min(Math.max(mh, h), mxh);
28306                         w = ow * (h/oh);
28307                         y += th - h;
28308                         x += tw - w;
28309                        break;
28310
28311                 }
28312             }
28313             if (pos == 'hdrag') {
28314                 w = ow;
28315             }
28316             this.proxy.setBounds(x, y, w, h);
28317             if(this.dynamic){
28318                 this.resizeElement();
28319             }
28320             }catch(e){}
28321         }
28322     },
28323
28324     // private
28325     handleOver : function(){
28326         if(this.enabled){
28327             this.el.addClass("x-resizable-over");
28328         }
28329     },
28330
28331     // private
28332     handleOut : function(){
28333         if(!this.resizing){
28334             this.el.removeClass("x-resizable-over");
28335         }
28336     },
28337
28338     /**
28339      * Returns the element this component is bound to.
28340      * @return {Roo.Element}
28341      */
28342     getEl : function(){
28343         return this.el;
28344     },
28345
28346     /**
28347      * Returns the resizeChild element (or null).
28348      * @return {Roo.Element}
28349      */
28350     getResizeChild : function(){
28351         return this.resizeChild;
28352     },
28353
28354     /**
28355      * Destroys this resizable. If the element was wrapped and
28356      * removeEl is not true then the element remains.
28357      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28358      */
28359     destroy : function(removeEl){
28360         this.proxy.remove();
28361         if(this.overlay){
28362             this.overlay.removeAllListeners();
28363             this.overlay.remove();
28364         }
28365         var ps = Roo.Resizable.positions;
28366         for(var k in ps){
28367             if(typeof ps[k] != "function" && this[ps[k]]){
28368                 var h = this[ps[k]];
28369                 h.el.removeAllListeners();
28370                 h.el.remove();
28371             }
28372         }
28373         if(removeEl){
28374             this.el.update("");
28375             this.el.remove();
28376         }
28377     }
28378 });
28379
28380 // private
28381 // hash to map config positions to true positions
28382 Roo.Resizable.positions = {
28383     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28384     hd: "hdrag"
28385 };
28386
28387 // private
28388 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28389     if(!this.tpl){
28390         // only initialize the template if resizable is used
28391         var tpl = Roo.DomHelper.createTemplate(
28392             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28393         );
28394         tpl.compile();
28395         Roo.Resizable.Handle.prototype.tpl = tpl;
28396     }
28397     this.position = pos;
28398     this.rz = rz;
28399     // show north drag fro topdra
28400     var handlepos = pos == 'hdrag' ? 'north' : pos;
28401     
28402     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28403     if (pos == 'hdrag') {
28404         this.el.setStyle('cursor', 'pointer');
28405     }
28406     this.el.unselectable();
28407     if(transparent){
28408         this.el.setOpacity(0);
28409     }
28410     this.el.on("mousedown", this.onMouseDown, this);
28411     if(!disableTrackOver){
28412         this.el.on("mouseover", this.onMouseOver, this);
28413         this.el.on("mouseout", this.onMouseOut, this);
28414     }
28415 };
28416
28417 // private
28418 Roo.Resizable.Handle.prototype = {
28419     afterResize : function(rz){
28420         // do nothing
28421     },
28422     // private
28423     onMouseDown : function(e){
28424         this.rz.onMouseDown(this, e);
28425     },
28426     // private
28427     onMouseOver : function(e){
28428         this.rz.handleOver(this, e);
28429     },
28430     // private
28431     onMouseOut : function(e){
28432         this.rz.handleOut(this, e);
28433     }
28434 };/*
28435  * Based on:
28436  * Ext JS Library 1.1.1
28437  * Copyright(c) 2006-2007, Ext JS, LLC.
28438  *
28439  * Originally Released Under LGPL - original licence link has changed is not relivant.
28440  *
28441  * Fork - LGPL
28442  * <script type="text/javascript">
28443  */
28444
28445 /**
28446  * @class Roo.Editor
28447  * @extends Roo.Component
28448  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28449  * @constructor
28450  * Create a new Editor
28451  * @param {Roo.form.Field} field The Field object (or descendant)
28452  * @param {Object} config The config object
28453  */
28454 Roo.Editor = function(field, config){
28455     Roo.Editor.superclass.constructor.call(this, config);
28456     this.field = field;
28457     this.addEvents({
28458         /**
28459              * @event beforestartedit
28460              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28461              * false from the handler of this event.
28462              * @param {Editor} this
28463              * @param {Roo.Element} boundEl The underlying element bound to this editor
28464              * @param {Mixed} value The field value being set
28465              */
28466         "beforestartedit" : true,
28467         /**
28468              * @event startedit
28469              * Fires when this editor is displayed
28470              * @param {Roo.Element} boundEl The underlying element bound to this editor
28471              * @param {Mixed} value The starting field value
28472              */
28473         "startedit" : true,
28474         /**
28475              * @event beforecomplete
28476              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28477              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28478              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28479              * event will not fire since no edit actually occurred.
28480              * @param {Editor} this
28481              * @param {Mixed} value The current field value
28482              * @param {Mixed} startValue The original field value
28483              */
28484         "beforecomplete" : true,
28485         /**
28486              * @event complete
28487              * Fires after editing is complete and any changed value has been written to the underlying field.
28488              * @param {Editor} this
28489              * @param {Mixed} value The current field value
28490              * @param {Mixed} startValue The original field value
28491              */
28492         "complete" : true,
28493         /**
28494          * @event specialkey
28495          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28496          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28497          * @param {Roo.form.Field} this
28498          * @param {Roo.EventObject} e The event object
28499          */
28500         "specialkey" : true
28501     });
28502 };
28503
28504 Roo.extend(Roo.Editor, Roo.Component, {
28505     /**
28506      * @cfg {Boolean/String} autosize
28507      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28508      * or "height" to adopt the height only (defaults to false)
28509      */
28510     /**
28511      * @cfg {Boolean} revertInvalid
28512      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28513      * validation fails (defaults to true)
28514      */
28515     /**
28516      * @cfg {Boolean} ignoreNoChange
28517      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28518      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28519      * will never be ignored.
28520      */
28521     /**
28522      * @cfg {Boolean} hideEl
28523      * False to keep the bound element visible while the editor is displayed (defaults to true)
28524      */
28525     /**
28526      * @cfg {Mixed} value
28527      * The data value of the underlying field (defaults to "")
28528      */
28529     value : "",
28530     /**
28531      * @cfg {String} alignment
28532      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28533      */
28534     alignment: "c-c?",
28535     /**
28536      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28537      * for bottom-right shadow (defaults to "frame")
28538      */
28539     shadow : "frame",
28540     /**
28541      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28542      */
28543     constrain : false,
28544     /**
28545      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28546      */
28547     completeOnEnter : false,
28548     /**
28549      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28550      */
28551     cancelOnEsc : false,
28552     /**
28553      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28554      */
28555     updateEl : false,
28556
28557     // private
28558     onRender : function(ct, position){
28559         this.el = new Roo.Layer({
28560             shadow: this.shadow,
28561             cls: "x-editor",
28562             parentEl : ct,
28563             shim : this.shim,
28564             shadowOffset:4,
28565             id: this.id,
28566             constrain: this.constrain
28567         });
28568         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28569         if(this.field.msgTarget != 'title'){
28570             this.field.msgTarget = 'qtip';
28571         }
28572         this.field.render(this.el);
28573         if(Roo.isGecko){
28574             this.field.el.dom.setAttribute('autocomplete', 'off');
28575         }
28576         this.field.on("specialkey", this.onSpecialKey, this);
28577         if(this.swallowKeys){
28578             this.field.el.swallowEvent(['keydown','keypress']);
28579         }
28580         this.field.show();
28581         this.field.on("blur", this.onBlur, this);
28582         if(this.field.grow){
28583             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28584         }
28585     },
28586
28587     onSpecialKey : function(field, e)
28588     {
28589         //Roo.log('editor onSpecialKey');
28590         if(this.completeOnEnter && e.getKey() == e.ENTER){
28591             e.stopEvent();
28592             this.completeEdit();
28593             return;
28594         }
28595         // do not fire special key otherwise it might hide close the editor...
28596         if(e.getKey() == e.ENTER){    
28597             return;
28598         }
28599         if(this.cancelOnEsc && e.getKey() == e.ESC){
28600             this.cancelEdit();
28601             return;
28602         } 
28603         this.fireEvent('specialkey', field, e);
28604     
28605     },
28606
28607     /**
28608      * Starts the editing process and shows the editor.
28609      * @param {String/HTMLElement/Element} el The element to edit
28610      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28611       * to the innerHTML of el.
28612      */
28613     startEdit : function(el, value){
28614         if(this.editing){
28615             this.completeEdit();
28616         }
28617         this.boundEl = Roo.get(el);
28618         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28619         if(!this.rendered){
28620             this.render(this.parentEl || document.body);
28621         }
28622         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28623             return;
28624         }
28625         this.startValue = v;
28626         this.field.setValue(v);
28627         if(this.autoSize){
28628             var sz = this.boundEl.getSize();
28629             switch(this.autoSize){
28630                 case "width":
28631                 this.setSize(sz.width,  "");
28632                 break;
28633                 case "height":
28634                 this.setSize("",  sz.height);
28635                 break;
28636                 default:
28637                 this.setSize(sz.width,  sz.height);
28638             }
28639         }
28640         this.el.alignTo(this.boundEl, this.alignment);
28641         this.editing = true;
28642         if(Roo.QuickTips){
28643             Roo.QuickTips.disable();
28644         }
28645         this.show();
28646     },
28647
28648     /**
28649      * Sets the height and width of this editor.
28650      * @param {Number} width The new width
28651      * @param {Number} height The new height
28652      */
28653     setSize : function(w, h){
28654         this.field.setSize(w, h);
28655         if(this.el){
28656             this.el.sync();
28657         }
28658     },
28659
28660     /**
28661      * Realigns the editor to the bound field based on the current alignment config value.
28662      */
28663     realign : function(){
28664         this.el.alignTo(this.boundEl, this.alignment);
28665     },
28666
28667     /**
28668      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28669      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28670      */
28671     completeEdit : function(remainVisible){
28672         if(!this.editing){
28673             return;
28674         }
28675         var v = this.getValue();
28676         if(this.revertInvalid !== false && !this.field.isValid()){
28677             v = this.startValue;
28678             this.cancelEdit(true);
28679         }
28680         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28681             this.editing = false;
28682             this.hide();
28683             return;
28684         }
28685         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28686             this.editing = false;
28687             if(this.updateEl && this.boundEl){
28688                 this.boundEl.update(v);
28689             }
28690             if(remainVisible !== true){
28691                 this.hide();
28692             }
28693             this.fireEvent("complete", this, v, this.startValue);
28694         }
28695     },
28696
28697     // private
28698     onShow : function(){
28699         this.el.show();
28700         if(this.hideEl !== false){
28701             this.boundEl.hide();
28702         }
28703         this.field.show();
28704         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28705             this.fixIEFocus = true;
28706             this.deferredFocus.defer(50, this);
28707         }else{
28708             this.field.focus();
28709         }
28710         this.fireEvent("startedit", this.boundEl, this.startValue);
28711     },
28712
28713     deferredFocus : function(){
28714         if(this.editing){
28715             this.field.focus();
28716         }
28717     },
28718
28719     /**
28720      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28721      * reverted to the original starting value.
28722      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28723      * cancel (defaults to false)
28724      */
28725     cancelEdit : function(remainVisible){
28726         if(this.editing){
28727             this.setValue(this.startValue);
28728             if(remainVisible !== true){
28729                 this.hide();
28730             }
28731         }
28732     },
28733
28734     // private
28735     onBlur : function(){
28736         if(this.allowBlur !== true && this.editing){
28737             this.completeEdit();
28738         }
28739     },
28740
28741     // private
28742     onHide : function(){
28743         if(this.editing){
28744             this.completeEdit();
28745             return;
28746         }
28747         this.field.blur();
28748         if(this.field.collapse){
28749             this.field.collapse();
28750         }
28751         this.el.hide();
28752         if(this.hideEl !== false){
28753             this.boundEl.show();
28754         }
28755         if(Roo.QuickTips){
28756             Roo.QuickTips.enable();
28757         }
28758     },
28759
28760     /**
28761      * Sets the data value of the editor
28762      * @param {Mixed} value Any valid value supported by the underlying field
28763      */
28764     setValue : function(v){
28765         this.field.setValue(v);
28766     },
28767
28768     /**
28769      * Gets the data value of the editor
28770      * @return {Mixed} The data value
28771      */
28772     getValue : function(){
28773         return this.field.getValue();
28774     }
28775 });/*
28776  * Based on:
28777  * Ext JS Library 1.1.1
28778  * Copyright(c) 2006-2007, Ext JS, LLC.
28779  *
28780  * Originally Released Under LGPL - original licence link has changed is not relivant.
28781  *
28782  * Fork - LGPL
28783  * <script type="text/javascript">
28784  */
28785  
28786 /**
28787  * @class Roo.BasicDialog
28788  * @extends Roo.util.Observable
28789  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28790  * <pre><code>
28791 var dlg = new Roo.BasicDialog("my-dlg", {
28792     height: 200,
28793     width: 300,
28794     minHeight: 100,
28795     minWidth: 150,
28796     modal: true,
28797     proxyDrag: true,
28798     shadow: true
28799 });
28800 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28801 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28802 dlg.addButton('Cancel', dlg.hide, dlg);
28803 dlg.show();
28804 </code></pre>
28805   <b>A Dialog should always be a direct child of the body element.</b>
28806  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28807  * @cfg {String} title Default text to display in the title bar (defaults to null)
28808  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28809  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28810  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28811  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28812  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28813  * (defaults to null with no animation)
28814  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28815  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28816  * property for valid values (defaults to 'all')
28817  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28818  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28819  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28820  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28821  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28822  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28823  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28824  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28825  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28826  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28827  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28828  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28829  * draggable = true (defaults to false)
28830  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28831  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28832  * shadow (defaults to false)
28833  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28834  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28835  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28836  * @cfg {Array} buttons Array of buttons
28837  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28838  * @constructor
28839  * Create a new BasicDialog.
28840  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28841  * @param {Object} config Configuration options
28842  */
28843 Roo.BasicDialog = function(el, config){
28844     this.el = Roo.get(el);
28845     var dh = Roo.DomHelper;
28846     if(!this.el && config && config.autoCreate){
28847         if(typeof config.autoCreate == "object"){
28848             if(!config.autoCreate.id){
28849                 config.autoCreate.id = el;
28850             }
28851             this.el = dh.append(document.body,
28852                         config.autoCreate, true);
28853         }else{
28854             this.el = dh.append(document.body,
28855                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28856         }
28857     }
28858     el = this.el;
28859     el.setDisplayed(true);
28860     el.hide = this.hideAction;
28861     this.id = el.id;
28862     el.addClass("x-dlg");
28863
28864     Roo.apply(this, config);
28865
28866     this.proxy = el.createProxy("x-dlg-proxy");
28867     this.proxy.hide = this.hideAction;
28868     this.proxy.setOpacity(.5);
28869     this.proxy.hide();
28870
28871     if(config.width){
28872         el.setWidth(config.width);
28873     }
28874     if(config.height){
28875         el.setHeight(config.height);
28876     }
28877     this.size = el.getSize();
28878     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28879         this.xy = [config.x,config.y];
28880     }else{
28881         this.xy = el.getCenterXY(true);
28882     }
28883     /** The header element @type Roo.Element */
28884     this.header = el.child("> .x-dlg-hd");
28885     /** The body element @type Roo.Element */
28886     this.body = el.child("> .x-dlg-bd");
28887     /** The footer element @type Roo.Element */
28888     this.footer = el.child("> .x-dlg-ft");
28889
28890     if(!this.header){
28891         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28892     }
28893     if(!this.body){
28894         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28895     }
28896
28897     this.header.unselectable();
28898     if(this.title){
28899         this.header.update(this.title);
28900     }
28901     // this element allows the dialog to be focused for keyboard event
28902     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28903     this.focusEl.swallowEvent("click", true);
28904
28905     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28906
28907     // wrap the body and footer for special rendering
28908     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28909     if(this.footer){
28910         this.bwrap.dom.appendChild(this.footer.dom);
28911     }
28912
28913     this.bg = this.el.createChild({
28914         tag: "div", cls:"x-dlg-bg",
28915         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28916     });
28917     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28918
28919
28920     if(this.autoScroll !== false && !this.autoTabs){
28921         this.body.setStyle("overflow", "auto");
28922     }
28923
28924     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28925
28926     if(this.closable !== false){
28927         this.el.addClass("x-dlg-closable");
28928         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28929         this.close.on("click", this.closeClick, this);
28930         this.close.addClassOnOver("x-dlg-close-over");
28931     }
28932     if(this.collapsible !== false){
28933         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28934         this.collapseBtn.on("click", this.collapseClick, this);
28935         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28936         this.header.on("dblclick", this.collapseClick, this);
28937     }
28938     if(this.resizable !== false){
28939         this.el.addClass("x-dlg-resizable");
28940         this.resizer = new Roo.Resizable(el, {
28941             minWidth: this.minWidth || 80,
28942             minHeight:this.minHeight || 80,
28943             handles: this.resizeHandles || "all",
28944             pinned: true
28945         });
28946         this.resizer.on("beforeresize", this.beforeResize, this);
28947         this.resizer.on("resize", this.onResize, this);
28948     }
28949     if(this.draggable !== false){
28950         el.addClass("x-dlg-draggable");
28951         if (!this.proxyDrag) {
28952             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28953         }
28954         else {
28955             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28956         }
28957         dd.setHandleElId(this.header.id);
28958         dd.endDrag = this.endMove.createDelegate(this);
28959         dd.startDrag = this.startMove.createDelegate(this);
28960         dd.onDrag = this.onDrag.createDelegate(this);
28961         dd.scroll = false;
28962         this.dd = dd;
28963     }
28964     if(this.modal){
28965         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28966         this.mask.enableDisplayMode("block");
28967         this.mask.hide();
28968         this.el.addClass("x-dlg-modal");
28969     }
28970     if(this.shadow){
28971         this.shadow = new Roo.Shadow({
28972             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28973             offset : this.shadowOffset
28974         });
28975     }else{
28976         this.shadowOffset = 0;
28977     }
28978     if(Roo.useShims && this.shim !== false){
28979         this.shim = this.el.createShim();
28980         this.shim.hide = this.hideAction;
28981         this.shim.hide();
28982     }else{
28983         this.shim = false;
28984     }
28985     if(this.autoTabs){
28986         this.initTabs();
28987     }
28988     if (this.buttons) { 
28989         var bts= this.buttons;
28990         this.buttons = [];
28991         Roo.each(bts, function(b) {
28992             this.addButton(b);
28993         }, this);
28994     }
28995     
28996     
28997     this.addEvents({
28998         /**
28999          * @event keydown
29000          * Fires when a key is pressed
29001          * @param {Roo.BasicDialog} this
29002          * @param {Roo.EventObject} e
29003          */
29004         "keydown" : true,
29005         /**
29006          * @event move
29007          * Fires when this dialog is moved by the user.
29008          * @param {Roo.BasicDialog} this
29009          * @param {Number} x The new page X
29010          * @param {Number} y The new page Y
29011          */
29012         "move" : true,
29013         /**
29014          * @event resize
29015          * Fires when this dialog is resized by the user.
29016          * @param {Roo.BasicDialog} this
29017          * @param {Number} width The new width
29018          * @param {Number} height The new height
29019          */
29020         "resize" : true,
29021         /**
29022          * @event beforehide
29023          * Fires before this dialog is hidden.
29024          * @param {Roo.BasicDialog} this
29025          */
29026         "beforehide" : true,
29027         /**
29028          * @event hide
29029          * Fires when this dialog is hidden.
29030          * @param {Roo.BasicDialog} this
29031          */
29032         "hide" : true,
29033         /**
29034          * @event beforeshow
29035          * Fires before this dialog is shown.
29036          * @param {Roo.BasicDialog} this
29037          */
29038         "beforeshow" : true,
29039         /**
29040          * @event show
29041          * Fires when this dialog is shown.
29042          * @param {Roo.BasicDialog} this
29043          */
29044         "show" : true
29045     });
29046     el.on("keydown", this.onKeyDown, this);
29047     el.on("mousedown", this.toFront, this);
29048     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29049     this.el.hide();
29050     Roo.DialogManager.register(this);
29051     Roo.BasicDialog.superclass.constructor.call(this);
29052 };
29053
29054 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29055     shadowOffset: Roo.isIE ? 6 : 5,
29056     minHeight: 80,
29057     minWidth: 200,
29058     minButtonWidth: 75,
29059     defaultButton: null,
29060     buttonAlign: "right",
29061     tabTag: 'div',
29062     firstShow: true,
29063
29064     /**
29065      * Sets the dialog title text
29066      * @param {String} text The title text to display
29067      * @return {Roo.BasicDialog} this
29068      */
29069     setTitle : function(text){
29070         this.header.update(text);
29071         return this;
29072     },
29073
29074     // private
29075     closeClick : function(){
29076         this.hide();
29077     },
29078
29079     // private
29080     collapseClick : function(){
29081         this[this.collapsed ? "expand" : "collapse"]();
29082     },
29083
29084     /**
29085      * Collapses the dialog to its minimized state (only the title bar is visible).
29086      * Equivalent to the user clicking the collapse dialog button.
29087      */
29088     collapse : function(){
29089         if(!this.collapsed){
29090             this.collapsed = true;
29091             this.el.addClass("x-dlg-collapsed");
29092             this.restoreHeight = this.el.getHeight();
29093             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29094         }
29095     },
29096
29097     /**
29098      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29099      * clicking the expand dialog button.
29100      */
29101     expand : function(){
29102         if(this.collapsed){
29103             this.collapsed = false;
29104             this.el.removeClass("x-dlg-collapsed");
29105             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29106         }
29107     },
29108
29109     /**
29110      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29111      * @return {Roo.TabPanel} The tabs component
29112      */
29113     initTabs : function(){
29114         var tabs = this.getTabs();
29115         while(tabs.getTab(0)){
29116             tabs.removeTab(0);
29117         }
29118         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29119             var dom = el.dom;
29120             tabs.addTab(Roo.id(dom), dom.title);
29121             dom.title = "";
29122         });
29123         tabs.activate(0);
29124         return tabs;
29125     },
29126
29127     // private
29128     beforeResize : function(){
29129         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29130     },
29131
29132     // private
29133     onResize : function(){
29134         this.refreshSize();
29135         this.syncBodyHeight();
29136         this.adjustAssets();
29137         this.focus();
29138         this.fireEvent("resize", this, this.size.width, this.size.height);
29139     },
29140
29141     // private
29142     onKeyDown : function(e){
29143         if(this.isVisible()){
29144             this.fireEvent("keydown", this, e);
29145         }
29146     },
29147
29148     /**
29149      * Resizes the dialog.
29150      * @param {Number} width
29151      * @param {Number} height
29152      * @return {Roo.BasicDialog} this
29153      */
29154     resizeTo : function(width, height){
29155         this.el.setSize(width, height);
29156         this.size = {width: width, height: height};
29157         this.syncBodyHeight();
29158         if(this.fixedcenter){
29159             this.center();
29160         }
29161         if(this.isVisible()){
29162             this.constrainXY();
29163             this.adjustAssets();
29164         }
29165         this.fireEvent("resize", this, width, height);
29166         return this;
29167     },
29168
29169
29170     /**
29171      * Resizes the dialog to fit the specified content size.
29172      * @param {Number} width
29173      * @param {Number} height
29174      * @return {Roo.BasicDialog} this
29175      */
29176     setContentSize : function(w, h){
29177         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29178         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29179         //if(!this.el.isBorderBox()){
29180             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29181             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29182         //}
29183         if(this.tabs){
29184             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29185             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29186         }
29187         this.resizeTo(w, h);
29188         return this;
29189     },
29190
29191     /**
29192      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29193      * executed in response to a particular key being pressed while the dialog is active.
29194      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29195      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29196      * @param {Function} fn The function to call
29197      * @param {Object} scope (optional) The scope of the function
29198      * @return {Roo.BasicDialog} this
29199      */
29200     addKeyListener : function(key, fn, scope){
29201         var keyCode, shift, ctrl, alt;
29202         if(typeof key == "object" && !(key instanceof Array)){
29203             keyCode = key["key"];
29204             shift = key["shift"];
29205             ctrl = key["ctrl"];
29206             alt = key["alt"];
29207         }else{
29208             keyCode = key;
29209         }
29210         var handler = function(dlg, e){
29211             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29212                 var k = e.getKey();
29213                 if(keyCode instanceof Array){
29214                     for(var i = 0, len = keyCode.length; i < len; i++){
29215                         if(keyCode[i] == k){
29216                           fn.call(scope || window, dlg, k, e);
29217                           return;
29218                         }
29219                     }
29220                 }else{
29221                     if(k == keyCode){
29222                         fn.call(scope || window, dlg, k, e);
29223                     }
29224                 }
29225             }
29226         };
29227         this.on("keydown", handler);
29228         return this;
29229     },
29230
29231     /**
29232      * Returns the TabPanel component (creates it if it doesn't exist).
29233      * Note: If you wish to simply check for the existence of tabs without creating them,
29234      * check for a null 'tabs' property.
29235      * @return {Roo.TabPanel} The tabs component
29236      */
29237     getTabs : function(){
29238         if(!this.tabs){
29239             this.el.addClass("x-dlg-auto-tabs");
29240             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29241             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29242         }
29243         return this.tabs;
29244     },
29245
29246     /**
29247      * Adds a button to the footer section of the dialog.
29248      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29249      * object or a valid Roo.DomHelper element config
29250      * @param {Function} handler The function called when the button is clicked
29251      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29252      * @return {Roo.Button} The new button
29253      */
29254     addButton : function(config, handler, scope){
29255         var dh = Roo.DomHelper;
29256         if(!this.footer){
29257             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29258         }
29259         if(!this.btnContainer){
29260             var tb = this.footer.createChild({
29261
29262                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29263                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29264             }, null, true);
29265             this.btnContainer = tb.firstChild.firstChild.firstChild;
29266         }
29267         var bconfig = {
29268             handler: handler,
29269             scope: scope,
29270             minWidth: this.minButtonWidth,
29271             hideParent:true
29272         };
29273         if(typeof config == "string"){
29274             bconfig.text = config;
29275         }else{
29276             if(config.tag){
29277                 bconfig.dhconfig = config;
29278             }else{
29279                 Roo.apply(bconfig, config);
29280             }
29281         }
29282         var fc = false;
29283         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29284             bconfig.position = Math.max(0, bconfig.position);
29285             fc = this.btnContainer.childNodes[bconfig.position];
29286         }
29287          
29288         var btn = new Roo.Button(
29289             fc ? 
29290                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29291                 : this.btnContainer.appendChild(document.createElement("td")),
29292             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29293             bconfig
29294         );
29295         this.syncBodyHeight();
29296         if(!this.buttons){
29297             /**
29298              * Array of all the buttons that have been added to this dialog via addButton
29299              * @type Array
29300              */
29301             this.buttons = [];
29302         }
29303         this.buttons.push(btn);
29304         return btn;
29305     },
29306
29307     /**
29308      * Sets the default button to be focused when the dialog is displayed.
29309      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29310      * @return {Roo.BasicDialog} this
29311      */
29312     setDefaultButton : function(btn){
29313         this.defaultButton = btn;
29314         return this;
29315     },
29316
29317     // private
29318     getHeaderFooterHeight : function(safe){
29319         var height = 0;
29320         if(this.header){
29321            height += this.header.getHeight();
29322         }
29323         if(this.footer){
29324            var fm = this.footer.getMargins();
29325             height += (this.footer.getHeight()+fm.top+fm.bottom);
29326         }
29327         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29328         height += this.centerBg.getPadding("tb");
29329         return height;
29330     },
29331
29332     // private
29333     syncBodyHeight : function(){
29334         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29335         var height = this.size.height - this.getHeaderFooterHeight(false);
29336         bd.setHeight(height-bd.getMargins("tb"));
29337         var hh = this.header.getHeight();
29338         var h = this.size.height-hh;
29339         cb.setHeight(h);
29340         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29341         bw.setHeight(h-cb.getPadding("tb"));
29342         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29343         bd.setWidth(bw.getWidth(true));
29344         if(this.tabs){
29345             this.tabs.syncHeight();
29346             if(Roo.isIE){
29347                 this.tabs.el.repaint();
29348             }
29349         }
29350     },
29351
29352     /**
29353      * Restores the previous state of the dialog if Roo.state is configured.
29354      * @return {Roo.BasicDialog} this
29355      */
29356     restoreState : function(){
29357         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29358         if(box && box.width){
29359             this.xy = [box.x, box.y];
29360             this.resizeTo(box.width, box.height);
29361         }
29362         return this;
29363     },
29364
29365     // private
29366     beforeShow : function(){
29367         this.expand();
29368         if(this.fixedcenter){
29369             this.xy = this.el.getCenterXY(true);
29370         }
29371         if(this.modal){
29372             Roo.get(document.body).addClass("x-body-masked");
29373             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29374             this.mask.show();
29375         }
29376         this.constrainXY();
29377     },
29378
29379     // private
29380     animShow : function(){
29381         var b = Roo.get(this.animateTarget).getBox();
29382         this.proxy.setSize(b.width, b.height);
29383         this.proxy.setLocation(b.x, b.y);
29384         this.proxy.show();
29385         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29386                     true, .35, this.showEl.createDelegate(this));
29387     },
29388
29389     /**
29390      * Shows the dialog.
29391      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29392      * @return {Roo.BasicDialog} this
29393      */
29394     show : function(animateTarget){
29395         if (this.fireEvent("beforeshow", this) === false){
29396             return;
29397         }
29398         if(this.syncHeightBeforeShow){
29399             this.syncBodyHeight();
29400         }else if(this.firstShow){
29401             this.firstShow = false;
29402             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29403         }
29404         this.animateTarget = animateTarget || this.animateTarget;
29405         if(!this.el.isVisible()){
29406             this.beforeShow();
29407             if(this.animateTarget && Roo.get(this.animateTarget)){
29408                 this.animShow();
29409             }else{
29410                 this.showEl();
29411             }
29412         }
29413         return this;
29414     },
29415
29416     // private
29417     showEl : function(){
29418         this.proxy.hide();
29419         this.el.setXY(this.xy);
29420         this.el.show();
29421         this.adjustAssets(true);
29422         this.toFront();
29423         this.focus();
29424         // IE peekaboo bug - fix found by Dave Fenwick
29425         if(Roo.isIE){
29426             this.el.repaint();
29427         }
29428         this.fireEvent("show", this);
29429     },
29430
29431     /**
29432      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29433      * dialog itself will receive focus.
29434      */
29435     focus : function(){
29436         if(this.defaultButton){
29437             this.defaultButton.focus();
29438         }else{
29439             this.focusEl.focus();
29440         }
29441     },
29442
29443     // private
29444     constrainXY : function(){
29445         if(this.constraintoviewport !== false){
29446             if(!this.viewSize){
29447                 if(this.container){
29448                     var s = this.container.getSize();
29449                     this.viewSize = [s.width, s.height];
29450                 }else{
29451                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29452                 }
29453             }
29454             var s = Roo.get(this.container||document).getScroll();
29455
29456             var x = this.xy[0], y = this.xy[1];
29457             var w = this.size.width, h = this.size.height;
29458             var vw = this.viewSize[0], vh = this.viewSize[1];
29459             // only move it if it needs it
29460             var moved = false;
29461             // first validate right/bottom
29462             if(x + w > vw+s.left){
29463                 x = vw - w;
29464                 moved = true;
29465             }
29466             if(y + h > vh+s.top){
29467                 y = vh - h;
29468                 moved = true;
29469             }
29470             // then make sure top/left isn't negative
29471             if(x < s.left){
29472                 x = s.left;
29473                 moved = true;
29474             }
29475             if(y < s.top){
29476                 y = s.top;
29477                 moved = true;
29478             }
29479             if(moved){
29480                 // cache xy
29481                 this.xy = [x, y];
29482                 if(this.isVisible()){
29483                     this.el.setLocation(x, y);
29484                     this.adjustAssets();
29485                 }
29486             }
29487         }
29488     },
29489
29490     // private
29491     onDrag : function(){
29492         if(!this.proxyDrag){
29493             this.xy = this.el.getXY();
29494             this.adjustAssets();
29495         }
29496     },
29497
29498     // private
29499     adjustAssets : function(doShow){
29500         var x = this.xy[0], y = this.xy[1];
29501         var w = this.size.width, h = this.size.height;
29502         if(doShow === true){
29503             if(this.shadow){
29504                 this.shadow.show(this.el);
29505             }
29506             if(this.shim){
29507                 this.shim.show();
29508             }
29509         }
29510         if(this.shadow && this.shadow.isVisible()){
29511             this.shadow.show(this.el);
29512         }
29513         if(this.shim && this.shim.isVisible()){
29514             this.shim.setBounds(x, y, w, h);
29515         }
29516     },
29517
29518     // private
29519     adjustViewport : function(w, h){
29520         if(!w || !h){
29521             w = Roo.lib.Dom.getViewWidth();
29522             h = Roo.lib.Dom.getViewHeight();
29523         }
29524         // cache the size
29525         this.viewSize = [w, h];
29526         if(this.modal && this.mask.isVisible()){
29527             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29528             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29529         }
29530         if(this.isVisible()){
29531             this.constrainXY();
29532         }
29533     },
29534
29535     /**
29536      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29537      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29538      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29539      */
29540     destroy : function(removeEl){
29541         if(this.isVisible()){
29542             this.animateTarget = null;
29543             this.hide();
29544         }
29545         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29546         if(this.tabs){
29547             this.tabs.destroy(removeEl);
29548         }
29549         Roo.destroy(
29550              this.shim,
29551              this.proxy,
29552              this.resizer,
29553              this.close,
29554              this.mask
29555         );
29556         if(this.dd){
29557             this.dd.unreg();
29558         }
29559         if(this.buttons){
29560            for(var i = 0, len = this.buttons.length; i < len; i++){
29561                this.buttons[i].destroy();
29562            }
29563         }
29564         this.el.removeAllListeners();
29565         if(removeEl === true){
29566             this.el.update("");
29567             this.el.remove();
29568         }
29569         Roo.DialogManager.unregister(this);
29570     },
29571
29572     // private
29573     startMove : function(){
29574         if(this.proxyDrag){
29575             this.proxy.show();
29576         }
29577         if(this.constraintoviewport !== false){
29578             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29579         }
29580     },
29581
29582     // private
29583     endMove : function(){
29584         if(!this.proxyDrag){
29585             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29586         }else{
29587             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29588             this.proxy.hide();
29589         }
29590         this.refreshSize();
29591         this.adjustAssets();
29592         this.focus();
29593         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29594     },
29595
29596     /**
29597      * Brings this dialog to the front of any other visible dialogs
29598      * @return {Roo.BasicDialog} this
29599      */
29600     toFront : function(){
29601         Roo.DialogManager.bringToFront(this);
29602         return this;
29603     },
29604
29605     /**
29606      * Sends this dialog to the back (under) of any other visible dialogs
29607      * @return {Roo.BasicDialog} this
29608      */
29609     toBack : function(){
29610         Roo.DialogManager.sendToBack(this);
29611         return this;
29612     },
29613
29614     /**
29615      * Centers this dialog in the viewport
29616      * @return {Roo.BasicDialog} this
29617      */
29618     center : function(){
29619         var xy = this.el.getCenterXY(true);
29620         this.moveTo(xy[0], xy[1]);
29621         return this;
29622     },
29623
29624     /**
29625      * Moves the dialog's top-left corner to the specified point
29626      * @param {Number} x
29627      * @param {Number} y
29628      * @return {Roo.BasicDialog} this
29629      */
29630     moveTo : function(x, y){
29631         this.xy = [x,y];
29632         if(this.isVisible()){
29633             this.el.setXY(this.xy);
29634             this.adjustAssets();
29635         }
29636         return this;
29637     },
29638
29639     /**
29640      * Aligns the dialog to the specified element
29641      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29642      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29643      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29644      * @return {Roo.BasicDialog} this
29645      */
29646     alignTo : function(element, position, offsets){
29647         this.xy = this.el.getAlignToXY(element, position, offsets);
29648         if(this.isVisible()){
29649             this.el.setXY(this.xy);
29650             this.adjustAssets();
29651         }
29652         return this;
29653     },
29654
29655     /**
29656      * Anchors an element to another element and realigns it when the window is resized.
29657      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29658      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29659      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29660      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29661      * is a number, it is used as the buffer delay (defaults to 50ms).
29662      * @return {Roo.BasicDialog} this
29663      */
29664     anchorTo : function(el, alignment, offsets, monitorScroll){
29665         var action = function(){
29666             this.alignTo(el, alignment, offsets);
29667         };
29668         Roo.EventManager.onWindowResize(action, this);
29669         var tm = typeof monitorScroll;
29670         if(tm != 'undefined'){
29671             Roo.EventManager.on(window, 'scroll', action, this,
29672                 {buffer: tm == 'number' ? monitorScroll : 50});
29673         }
29674         action.call(this);
29675         return this;
29676     },
29677
29678     /**
29679      * Returns true if the dialog is visible
29680      * @return {Boolean}
29681      */
29682     isVisible : function(){
29683         return this.el.isVisible();
29684     },
29685
29686     // private
29687     animHide : function(callback){
29688         var b = Roo.get(this.animateTarget).getBox();
29689         this.proxy.show();
29690         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29691         this.el.hide();
29692         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29693                     this.hideEl.createDelegate(this, [callback]));
29694     },
29695
29696     /**
29697      * Hides the dialog.
29698      * @param {Function} callback (optional) Function to call when the dialog is hidden
29699      * @return {Roo.BasicDialog} this
29700      */
29701     hide : function(callback){
29702         if (this.fireEvent("beforehide", this) === false){
29703             return;
29704         }
29705         if(this.shadow){
29706             this.shadow.hide();
29707         }
29708         if(this.shim) {
29709           this.shim.hide();
29710         }
29711         // sometimes animateTarget seems to get set.. causing problems...
29712         // this just double checks..
29713         if(this.animateTarget && Roo.get(this.animateTarget)) {
29714            this.animHide(callback);
29715         }else{
29716             this.el.hide();
29717             this.hideEl(callback);
29718         }
29719         return this;
29720     },
29721
29722     // private
29723     hideEl : function(callback){
29724         this.proxy.hide();
29725         if(this.modal){
29726             this.mask.hide();
29727             Roo.get(document.body).removeClass("x-body-masked");
29728         }
29729         this.fireEvent("hide", this);
29730         if(typeof callback == "function"){
29731             callback();
29732         }
29733     },
29734
29735     // private
29736     hideAction : function(){
29737         this.setLeft("-10000px");
29738         this.setTop("-10000px");
29739         this.setStyle("visibility", "hidden");
29740     },
29741
29742     // private
29743     refreshSize : function(){
29744         this.size = this.el.getSize();
29745         this.xy = this.el.getXY();
29746         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29747     },
29748
29749     // private
29750     // z-index is managed by the DialogManager and may be overwritten at any time
29751     setZIndex : function(index){
29752         if(this.modal){
29753             this.mask.setStyle("z-index", index);
29754         }
29755         if(this.shim){
29756             this.shim.setStyle("z-index", ++index);
29757         }
29758         if(this.shadow){
29759             this.shadow.setZIndex(++index);
29760         }
29761         this.el.setStyle("z-index", ++index);
29762         if(this.proxy){
29763             this.proxy.setStyle("z-index", ++index);
29764         }
29765         if(this.resizer){
29766             this.resizer.proxy.setStyle("z-index", ++index);
29767         }
29768
29769         this.lastZIndex = index;
29770     },
29771
29772     /**
29773      * Returns the element for this dialog
29774      * @return {Roo.Element} The underlying dialog Element
29775      */
29776     getEl : function(){
29777         return this.el;
29778     }
29779 });
29780
29781 /**
29782  * @class Roo.DialogManager
29783  * Provides global access to BasicDialogs that have been created and
29784  * support for z-indexing (layering) multiple open dialogs.
29785  */
29786 Roo.DialogManager = function(){
29787     var list = {};
29788     var accessList = [];
29789     var front = null;
29790
29791     // private
29792     var sortDialogs = function(d1, d2){
29793         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29794     };
29795
29796     // private
29797     var orderDialogs = function(){
29798         accessList.sort(sortDialogs);
29799         var seed = Roo.DialogManager.zseed;
29800         for(var i = 0, len = accessList.length; i < len; i++){
29801             var dlg = accessList[i];
29802             if(dlg){
29803                 dlg.setZIndex(seed + (i*10));
29804             }
29805         }
29806     };
29807
29808     return {
29809         /**
29810          * The starting z-index for BasicDialogs (defaults to 9000)
29811          * @type Number The z-index value
29812          */
29813         zseed : 9000,
29814
29815         // private
29816         register : function(dlg){
29817             list[dlg.id] = dlg;
29818             accessList.push(dlg);
29819         },
29820
29821         // private
29822         unregister : function(dlg){
29823             delete list[dlg.id];
29824             var i=0;
29825             var len=0;
29826             if(!accessList.indexOf){
29827                 for(  i = 0, len = accessList.length; i < len; i++){
29828                     if(accessList[i] == dlg){
29829                         accessList.splice(i, 1);
29830                         return;
29831                     }
29832                 }
29833             }else{
29834                  i = accessList.indexOf(dlg);
29835                 if(i != -1){
29836                     accessList.splice(i, 1);
29837                 }
29838             }
29839         },
29840
29841         /**
29842          * Gets a registered dialog by id
29843          * @param {String/Object} id The id of the dialog or a dialog
29844          * @return {Roo.BasicDialog} this
29845          */
29846         get : function(id){
29847             return typeof id == "object" ? id : list[id];
29848         },
29849
29850         /**
29851          * Brings the specified dialog to the front
29852          * @param {String/Object} dlg The id of the dialog or a dialog
29853          * @return {Roo.BasicDialog} this
29854          */
29855         bringToFront : function(dlg){
29856             dlg = this.get(dlg);
29857             if(dlg != front){
29858                 front = dlg;
29859                 dlg._lastAccess = new Date().getTime();
29860                 orderDialogs();
29861             }
29862             return dlg;
29863         },
29864
29865         /**
29866          * Sends the specified dialog to the back
29867          * @param {String/Object} dlg The id of the dialog or a dialog
29868          * @return {Roo.BasicDialog} this
29869          */
29870         sendToBack : function(dlg){
29871             dlg = this.get(dlg);
29872             dlg._lastAccess = -(new Date().getTime());
29873             orderDialogs();
29874             return dlg;
29875         },
29876
29877         /**
29878          * Hides all dialogs
29879          */
29880         hideAll : function(){
29881             for(var id in list){
29882                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29883                     list[id].hide();
29884                 }
29885             }
29886         }
29887     };
29888 }();
29889
29890 /**
29891  * @class Roo.LayoutDialog
29892  * @extends Roo.BasicDialog
29893  * Dialog which provides adjustments for working with a layout in a Dialog.
29894  * Add your necessary layout config options to the dialog's config.<br>
29895  * Example usage (including a nested layout):
29896  * <pre><code>
29897 if(!dialog){
29898     dialog = new Roo.LayoutDialog("download-dlg", {
29899         modal: true,
29900         width:600,
29901         height:450,
29902         shadow:true,
29903         minWidth:500,
29904         minHeight:350,
29905         autoTabs:true,
29906         proxyDrag:true,
29907         // layout config merges with the dialog config
29908         center:{
29909             tabPosition: "top",
29910             alwaysShowTabs: true
29911         }
29912     });
29913     dialog.addKeyListener(27, dialog.hide, dialog);
29914     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29915     dialog.addButton("Build It!", this.getDownload, this);
29916
29917     // we can even add nested layouts
29918     var innerLayout = new Roo.BorderLayout("dl-inner", {
29919         east: {
29920             initialSize: 200,
29921             autoScroll:true,
29922             split:true
29923         },
29924         center: {
29925             autoScroll:true
29926         }
29927     });
29928     innerLayout.beginUpdate();
29929     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29930     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29931     innerLayout.endUpdate(true);
29932
29933     var layout = dialog.getLayout();
29934     layout.beginUpdate();
29935     layout.add("center", new Roo.ContentPanel("standard-panel",
29936                         {title: "Download the Source", fitToFrame:true}));
29937     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29938                {title: "Build your own roo.js"}));
29939     layout.getRegion("center").showPanel(sp);
29940     layout.endUpdate();
29941 }
29942 </code></pre>
29943     * @constructor
29944     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29945     * @param {Object} config configuration options
29946   */
29947 Roo.LayoutDialog = function(el, cfg){
29948     
29949     var config=  cfg;
29950     if (typeof(cfg) == 'undefined') {
29951         config = Roo.apply({}, el);
29952         // not sure why we use documentElement here.. - it should always be body.
29953         // IE7 borks horribly if we use documentElement.
29954         // webkit also does not like documentElement - it creates a body element...
29955         el = Roo.get( document.body || document.documentElement ).createChild();
29956         //config.autoCreate = true;
29957     }
29958     
29959     
29960     config.autoTabs = false;
29961     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29962     this.body.setStyle({overflow:"hidden", position:"relative"});
29963     this.layout = new Roo.BorderLayout(this.body.dom, config);
29964     this.layout.monitorWindowResize = false;
29965     this.el.addClass("x-dlg-auto-layout");
29966     // fix case when center region overwrites center function
29967     this.center = Roo.BasicDialog.prototype.center;
29968     this.on("show", this.layout.layout, this.layout, true);
29969     if (config.items) {
29970         var xitems = config.items;
29971         delete config.items;
29972         Roo.each(xitems, this.addxtype, this);
29973     }
29974     
29975     
29976 };
29977 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29978     /**
29979      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29980      * @deprecated
29981      */
29982     endUpdate : function(){
29983         this.layout.endUpdate();
29984     },
29985
29986     /**
29987      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29988      *  @deprecated
29989      */
29990     beginUpdate : function(){
29991         this.layout.beginUpdate();
29992     },
29993
29994     /**
29995      * Get the BorderLayout for this dialog
29996      * @return {Roo.BorderLayout}
29997      */
29998     getLayout : function(){
29999         return this.layout;
30000     },
30001
30002     showEl : function(){
30003         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30004         if(Roo.isIE7){
30005             this.layout.layout();
30006         }
30007     },
30008
30009     // private
30010     // Use the syncHeightBeforeShow config option to control this automatically
30011     syncBodyHeight : function(){
30012         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30013         if(this.layout){this.layout.layout();}
30014     },
30015     
30016       /**
30017      * Add an xtype element (actually adds to the layout.)
30018      * @return {Object} xdata xtype object data.
30019      */
30020     
30021     addxtype : function(c) {
30022         return this.layout.addxtype(c);
30023     }
30024 });/*
30025  * Based on:
30026  * Ext JS Library 1.1.1
30027  * Copyright(c) 2006-2007, Ext JS, LLC.
30028  *
30029  * Originally Released Under LGPL - original licence link has changed is not relivant.
30030  *
30031  * Fork - LGPL
30032  * <script type="text/javascript">
30033  */
30034  
30035 /**
30036  * @class Roo.MessageBox
30037  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30038  * Example usage:
30039  *<pre><code>
30040 // Basic alert:
30041 Roo.Msg.alert('Status', 'Changes saved successfully.');
30042
30043 // Prompt for user data:
30044 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30045     if (btn == 'ok'){
30046         // process text value...
30047     }
30048 });
30049
30050 // Show a dialog using config options:
30051 Roo.Msg.show({
30052    title:'Save Changes?',
30053    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30054    buttons: Roo.Msg.YESNOCANCEL,
30055    fn: processResult,
30056    animEl: 'elId'
30057 });
30058 </code></pre>
30059  * @singleton
30060  */
30061 Roo.MessageBox = function(){
30062     var dlg, opt, mask, waitTimer;
30063     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30064     var buttons, activeTextEl, bwidth;
30065
30066     // private
30067     var handleButton = function(button){
30068         dlg.hide();
30069         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30070     };
30071
30072     // private
30073     var handleHide = function(){
30074         if(opt && opt.cls){
30075             dlg.el.removeClass(opt.cls);
30076         }
30077         if(waitTimer){
30078             Roo.TaskMgr.stop(waitTimer);
30079             waitTimer = null;
30080         }
30081     };
30082
30083     // private
30084     var updateButtons = function(b){
30085         var width = 0;
30086         if(!b){
30087             buttons["ok"].hide();
30088             buttons["cancel"].hide();
30089             buttons["yes"].hide();
30090             buttons["no"].hide();
30091             dlg.footer.dom.style.display = 'none';
30092             return width;
30093         }
30094         dlg.footer.dom.style.display = '';
30095         for(var k in buttons){
30096             if(typeof buttons[k] != "function"){
30097                 if(b[k]){
30098                     buttons[k].show();
30099                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30100                     width += buttons[k].el.getWidth()+15;
30101                 }else{
30102                     buttons[k].hide();
30103                 }
30104             }
30105         }
30106         return width;
30107     };
30108
30109     // private
30110     var handleEsc = function(d, k, e){
30111         if(opt && opt.closable !== false){
30112             dlg.hide();
30113         }
30114         if(e){
30115             e.stopEvent();
30116         }
30117     };
30118
30119     return {
30120         /**
30121          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30122          * @return {Roo.BasicDialog} The BasicDialog element
30123          */
30124         getDialog : function(){
30125            if(!dlg){
30126                 dlg = new Roo.BasicDialog("x-msg-box", {
30127                     autoCreate : true,
30128                     shadow: true,
30129                     draggable: true,
30130                     resizable:false,
30131                     constraintoviewport:false,
30132                     fixedcenter:true,
30133                     collapsible : false,
30134                     shim:true,
30135                     modal: true,
30136                     width:400, height:100,
30137                     buttonAlign:"center",
30138                     closeClick : function(){
30139                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30140                             handleButton("no");
30141                         }else{
30142                             handleButton("cancel");
30143                         }
30144                     }
30145                 });
30146                 dlg.on("hide", handleHide);
30147                 mask = dlg.mask;
30148                 dlg.addKeyListener(27, handleEsc);
30149                 buttons = {};
30150                 var bt = this.buttonText;
30151                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30152                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30153                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30154                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30155                 bodyEl = dlg.body.createChild({
30156
30157                     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>'
30158                 });
30159                 msgEl = bodyEl.dom.firstChild;
30160                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30161                 textboxEl.enableDisplayMode();
30162                 textboxEl.addKeyListener([10,13], function(){
30163                     if(dlg.isVisible() && opt && opt.buttons){
30164                         if(opt.buttons.ok){
30165                             handleButton("ok");
30166                         }else if(opt.buttons.yes){
30167                             handleButton("yes");
30168                         }
30169                     }
30170                 });
30171                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30172                 textareaEl.enableDisplayMode();
30173                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30174                 progressEl.enableDisplayMode();
30175                 var pf = progressEl.dom.firstChild;
30176                 if (pf) {
30177                     pp = Roo.get(pf.firstChild);
30178                     pp.setHeight(pf.offsetHeight);
30179                 }
30180                 
30181             }
30182             return dlg;
30183         },
30184
30185         /**
30186          * Updates the message box body text
30187          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30188          * the XHTML-compliant non-breaking space character '&amp;#160;')
30189          * @return {Roo.MessageBox} This message box
30190          */
30191         updateText : function(text){
30192             if(!dlg.isVisible() && !opt.width){
30193                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30194             }
30195             msgEl.innerHTML = text || '&#160;';
30196       
30197             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30198             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30199             var w = Math.max(
30200                     Math.min(opt.width || cw , this.maxWidth), 
30201                     Math.max(opt.minWidth || this.minWidth, bwidth)
30202             );
30203             if(opt.prompt){
30204                 activeTextEl.setWidth(w);
30205             }
30206             if(dlg.isVisible()){
30207                 dlg.fixedcenter = false;
30208             }
30209             // to big, make it scroll. = But as usual stupid IE does not support
30210             // !important..
30211             
30212             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30213                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30214                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30215             } else {
30216                 bodyEl.dom.style.height = '';
30217                 bodyEl.dom.style.overflowY = '';
30218             }
30219             if (cw > w) {
30220                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30221             } else {
30222                 bodyEl.dom.style.overflowX = '';
30223             }
30224             
30225             dlg.setContentSize(w, bodyEl.getHeight());
30226             if(dlg.isVisible()){
30227                 dlg.fixedcenter = true;
30228             }
30229             return this;
30230         },
30231
30232         /**
30233          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30234          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30235          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30236          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30237          * @return {Roo.MessageBox} This message box
30238          */
30239         updateProgress : function(value, text){
30240             if(text){
30241                 this.updateText(text);
30242             }
30243             if (pp) { // weird bug on my firefox - for some reason this is not defined
30244                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30245             }
30246             return this;
30247         },        
30248
30249         /**
30250          * Returns true if the message box is currently displayed
30251          * @return {Boolean} True if the message box is visible, else false
30252          */
30253         isVisible : function(){
30254             return dlg && dlg.isVisible();  
30255         },
30256
30257         /**
30258          * Hides the message box if it is displayed
30259          */
30260         hide : function(){
30261             if(this.isVisible()){
30262                 dlg.hide();
30263             }  
30264         },
30265
30266         /**
30267          * Displays a new message box, or reinitializes an existing message box, based on the config options
30268          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30269          * The following config object properties are supported:
30270          * <pre>
30271 Property    Type             Description
30272 ----------  ---------------  ------------------------------------------------------------------------------------
30273 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30274                                    closes (defaults to undefined)
30275 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30276                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30277 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30278                                    progress and wait dialogs will ignore this property and always hide the
30279                                    close button as they can only be closed programmatically.
30280 cls               String           A custom CSS class to apply to the message box element
30281 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30282                                    displayed (defaults to 75)
30283 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30284                                    function will be btn (the name of the button that was clicked, if applicable,
30285                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30286                                    Progress and wait dialogs will ignore this option since they do not respond to
30287                                    user actions and can only be closed programmatically, so any required function
30288                                    should be called by the same code after it closes the dialog.
30289 icon              String           A CSS class that provides a background image to be used as an icon for
30290                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30291 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30292 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30293 modal             Boolean          False to allow user interaction with the page while the message box is
30294                                    displayed (defaults to true)
30295 msg               String           A string that will replace the existing message box body text (defaults
30296                                    to the XHTML-compliant non-breaking space character '&#160;')
30297 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30298 progress          Boolean          True to display a progress bar (defaults to false)
30299 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30300 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30301 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30302 title             String           The title text
30303 value             String           The string value to set into the active textbox element if displayed
30304 wait              Boolean          True to display a progress bar (defaults to false)
30305 width             Number           The width of the dialog in pixels
30306 </pre>
30307          *
30308          * Example usage:
30309          * <pre><code>
30310 Roo.Msg.show({
30311    title: 'Address',
30312    msg: 'Please enter your address:',
30313    width: 300,
30314    buttons: Roo.MessageBox.OKCANCEL,
30315    multiline: true,
30316    fn: saveAddress,
30317    animEl: 'addAddressBtn'
30318 });
30319 </code></pre>
30320          * @param {Object} config Configuration options
30321          * @return {Roo.MessageBox} This message box
30322          */
30323         show : function(options)
30324         {
30325             
30326             // this causes nightmares if you show one dialog after another
30327             // especially on callbacks..
30328              
30329             if(this.isVisible()){
30330                 
30331                 this.hide();
30332                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30333                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30334                 Roo.log("New Dialog Message:" +  options.msg )
30335                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30336                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30337                 
30338             }
30339             var d = this.getDialog();
30340             opt = options;
30341             d.setTitle(opt.title || "&#160;");
30342             d.close.setDisplayed(opt.closable !== false);
30343             activeTextEl = textboxEl;
30344             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30345             if(opt.prompt){
30346                 if(opt.multiline){
30347                     textboxEl.hide();
30348                     textareaEl.show();
30349                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30350                         opt.multiline : this.defaultTextHeight);
30351                     activeTextEl = textareaEl;
30352                 }else{
30353                     textboxEl.show();
30354                     textareaEl.hide();
30355                 }
30356             }else{
30357                 textboxEl.hide();
30358                 textareaEl.hide();
30359             }
30360             progressEl.setDisplayed(opt.progress === true);
30361             this.updateProgress(0);
30362             activeTextEl.dom.value = opt.value || "";
30363             if(opt.prompt){
30364                 dlg.setDefaultButton(activeTextEl);
30365             }else{
30366                 var bs = opt.buttons;
30367                 var db = null;
30368                 if(bs && bs.ok){
30369                     db = buttons["ok"];
30370                 }else if(bs && bs.yes){
30371                     db = buttons["yes"];
30372                 }
30373                 dlg.setDefaultButton(db);
30374             }
30375             bwidth = updateButtons(opt.buttons);
30376             this.updateText(opt.msg);
30377             if(opt.cls){
30378                 d.el.addClass(opt.cls);
30379             }
30380             d.proxyDrag = opt.proxyDrag === true;
30381             d.modal = opt.modal !== false;
30382             d.mask = opt.modal !== false ? mask : false;
30383             if(!d.isVisible()){
30384                 // force it to the end of the z-index stack so it gets a cursor in FF
30385                 document.body.appendChild(dlg.el.dom);
30386                 d.animateTarget = null;
30387                 d.show(options.animEl);
30388             }
30389             return this;
30390         },
30391
30392         /**
30393          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30394          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30395          * and closing the message box when the process is complete.
30396          * @param {String} title The title bar text
30397          * @param {String} msg The message box body text
30398          * @return {Roo.MessageBox} This message box
30399          */
30400         progress : function(title, msg){
30401             this.show({
30402                 title : title,
30403                 msg : msg,
30404                 buttons: false,
30405                 progress:true,
30406                 closable:false,
30407                 minWidth: this.minProgressWidth,
30408                 modal : true
30409             });
30410             return this;
30411         },
30412
30413         /**
30414          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30415          * If a callback function is passed it will be called after the user clicks the button, and the
30416          * id of the button that was clicked will be passed as the only parameter to the callback
30417          * (could also be the top-right close button).
30418          * @param {String} title The title bar text
30419          * @param {String} msg The message box body text
30420          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30421          * @param {Object} scope (optional) The scope of the callback function
30422          * @return {Roo.MessageBox} This message box
30423          */
30424         alert : function(title, msg, fn, scope){
30425             this.show({
30426                 title : title,
30427                 msg : msg,
30428                 buttons: this.OK,
30429                 fn: fn,
30430                 scope : scope,
30431                 modal : true
30432             });
30433             return this;
30434         },
30435
30436         /**
30437          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30438          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30439          * You are responsible for closing the message box when the process is complete.
30440          * @param {String} msg The message box body text
30441          * @param {String} title (optional) The title bar text
30442          * @return {Roo.MessageBox} This message box
30443          */
30444         wait : function(msg, title){
30445             this.show({
30446                 title : title,
30447                 msg : msg,
30448                 buttons: false,
30449                 closable:false,
30450                 progress:true,
30451                 modal:true,
30452                 width:300,
30453                 wait:true
30454             });
30455             waitTimer = Roo.TaskMgr.start({
30456                 run: function(i){
30457                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30458                 },
30459                 interval: 1000
30460             });
30461             return this;
30462         },
30463
30464         /**
30465          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30466          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30467          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30468          * @param {String} title The title bar text
30469          * @param {String} msg The message box body text
30470          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30471          * @param {Object} scope (optional) The scope of the callback function
30472          * @return {Roo.MessageBox} This message box
30473          */
30474         confirm : function(title, msg, fn, scope){
30475             this.show({
30476                 title : title,
30477                 msg : msg,
30478                 buttons: this.YESNO,
30479                 fn: fn,
30480                 scope : scope,
30481                 modal : true
30482             });
30483             return this;
30484         },
30485
30486         /**
30487          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30488          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30489          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30490          * (could also be the top-right close button) and the text that was entered will be passed as the two
30491          * parameters to the callback.
30492          * @param {String} title The title bar text
30493          * @param {String} msg The message box body text
30494          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30495          * @param {Object} scope (optional) The scope of the callback function
30496          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30497          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30498          * @return {Roo.MessageBox} This message box
30499          */
30500         prompt : function(title, msg, fn, scope, multiline){
30501             this.show({
30502                 title : title,
30503                 msg : msg,
30504                 buttons: this.OKCANCEL,
30505                 fn: fn,
30506                 minWidth:250,
30507                 scope : scope,
30508                 prompt:true,
30509                 multiline: multiline,
30510                 modal : true
30511             });
30512             return this;
30513         },
30514
30515         /**
30516          * Button config that displays a single OK button
30517          * @type Object
30518          */
30519         OK : {ok:true},
30520         /**
30521          * Button config that displays Yes and No buttons
30522          * @type Object
30523          */
30524         YESNO : {yes:true, no:true},
30525         /**
30526          * Button config that displays OK and Cancel buttons
30527          * @type Object
30528          */
30529         OKCANCEL : {ok:true, cancel:true},
30530         /**
30531          * Button config that displays Yes, No and Cancel buttons
30532          * @type Object
30533          */
30534         YESNOCANCEL : {yes:true, no:true, cancel:true},
30535
30536         /**
30537          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30538          * @type Number
30539          */
30540         defaultTextHeight : 75,
30541         /**
30542          * The maximum width in pixels of the message box (defaults to 600)
30543          * @type Number
30544          */
30545         maxWidth : 600,
30546         /**
30547          * The minimum width in pixels of the message box (defaults to 100)
30548          * @type Number
30549          */
30550         minWidth : 100,
30551         /**
30552          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30553          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30554          * @type Number
30555          */
30556         minProgressWidth : 250,
30557         /**
30558          * An object containing the default button text strings that can be overriden for localized language support.
30559          * Supported properties are: ok, cancel, yes and no.
30560          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30561          * @type Object
30562          */
30563         buttonText : {
30564             ok : "OK",
30565             cancel : "Cancel",
30566             yes : "Yes",
30567             no : "No"
30568         }
30569     };
30570 }();
30571
30572 /**
30573  * Shorthand for {@link Roo.MessageBox}
30574  */
30575 Roo.Msg = Roo.MessageBox;/*
30576  * Based on:
30577  * Ext JS Library 1.1.1
30578  * Copyright(c) 2006-2007, Ext JS, LLC.
30579  *
30580  * Originally Released Under LGPL - original licence link has changed is not relivant.
30581  *
30582  * Fork - LGPL
30583  * <script type="text/javascript">
30584  */
30585 /**
30586  * @class Roo.QuickTips
30587  * Provides attractive and customizable tooltips for any element.
30588  * @singleton
30589  */
30590 Roo.QuickTips = function(){
30591     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30592     var ce, bd, xy, dd;
30593     var visible = false, disabled = true, inited = false;
30594     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30595     
30596     var onOver = function(e){
30597         if(disabled){
30598             return;
30599         }
30600         var t = e.getTarget();
30601         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30602             return;
30603         }
30604         if(ce && t == ce.el){
30605             clearTimeout(hideProc);
30606             return;
30607         }
30608         if(t && tagEls[t.id]){
30609             tagEls[t.id].el = t;
30610             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30611             return;
30612         }
30613         var ttp, et = Roo.fly(t);
30614         var ns = cfg.namespace;
30615         if(tm.interceptTitles && t.title){
30616             ttp = t.title;
30617             t.qtip = ttp;
30618             t.removeAttribute("title");
30619             e.preventDefault();
30620         }else{
30621             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30622         }
30623         if(ttp){
30624             showProc = show.defer(tm.showDelay, tm, [{
30625                 el: t, 
30626                 text: ttp, 
30627                 width: et.getAttributeNS(ns, cfg.width),
30628                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30629                 title: et.getAttributeNS(ns, cfg.title),
30630                     cls: et.getAttributeNS(ns, cfg.cls)
30631             }]);
30632         }
30633     };
30634     
30635     var onOut = function(e){
30636         clearTimeout(showProc);
30637         var t = e.getTarget();
30638         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30639             hideProc = setTimeout(hide, tm.hideDelay);
30640         }
30641     };
30642     
30643     var onMove = function(e){
30644         if(disabled){
30645             return;
30646         }
30647         xy = e.getXY();
30648         xy[1] += 18;
30649         if(tm.trackMouse && ce){
30650             el.setXY(xy);
30651         }
30652     };
30653     
30654     var onDown = function(e){
30655         clearTimeout(showProc);
30656         clearTimeout(hideProc);
30657         if(!e.within(el)){
30658             if(tm.hideOnClick){
30659                 hide();
30660                 tm.disable();
30661                 tm.enable.defer(100, tm);
30662             }
30663         }
30664     };
30665     
30666     var getPad = function(){
30667         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30668     };
30669
30670     var show = function(o){
30671         if(disabled){
30672             return;
30673         }
30674         clearTimeout(dismissProc);
30675         ce = o;
30676         if(removeCls){ // in case manually hidden
30677             el.removeClass(removeCls);
30678             removeCls = null;
30679         }
30680         if(ce.cls){
30681             el.addClass(ce.cls);
30682             removeCls = ce.cls;
30683         }
30684         if(ce.title){
30685             tipTitle.update(ce.title);
30686             tipTitle.show();
30687         }else{
30688             tipTitle.update('');
30689             tipTitle.hide();
30690         }
30691         el.dom.style.width  = tm.maxWidth+'px';
30692         //tipBody.dom.style.width = '';
30693         tipBodyText.update(o.text);
30694         var p = getPad(), w = ce.width;
30695         if(!w){
30696             var td = tipBodyText.dom;
30697             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30698             if(aw > tm.maxWidth){
30699                 w = tm.maxWidth;
30700             }else if(aw < tm.minWidth){
30701                 w = tm.minWidth;
30702             }else{
30703                 w = aw;
30704             }
30705         }
30706         //tipBody.setWidth(w);
30707         el.setWidth(parseInt(w, 10) + p);
30708         if(ce.autoHide === false){
30709             close.setDisplayed(true);
30710             if(dd){
30711                 dd.unlock();
30712             }
30713         }else{
30714             close.setDisplayed(false);
30715             if(dd){
30716                 dd.lock();
30717             }
30718         }
30719         if(xy){
30720             el.avoidY = xy[1]-18;
30721             el.setXY(xy);
30722         }
30723         if(tm.animate){
30724             el.setOpacity(.1);
30725             el.setStyle("visibility", "visible");
30726             el.fadeIn({callback: afterShow});
30727         }else{
30728             afterShow();
30729         }
30730     };
30731     
30732     var afterShow = function(){
30733         if(ce){
30734             el.show();
30735             esc.enable();
30736             if(tm.autoDismiss && ce.autoHide !== false){
30737                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30738             }
30739         }
30740     };
30741     
30742     var hide = function(noanim){
30743         clearTimeout(dismissProc);
30744         clearTimeout(hideProc);
30745         ce = null;
30746         if(el.isVisible()){
30747             esc.disable();
30748             if(noanim !== true && tm.animate){
30749                 el.fadeOut({callback: afterHide});
30750             }else{
30751                 afterHide();
30752             } 
30753         }
30754     };
30755     
30756     var afterHide = function(){
30757         el.hide();
30758         if(removeCls){
30759             el.removeClass(removeCls);
30760             removeCls = null;
30761         }
30762     };
30763     
30764     return {
30765         /**
30766         * @cfg {Number} minWidth
30767         * The minimum width of the quick tip (defaults to 40)
30768         */
30769        minWidth : 40,
30770         /**
30771         * @cfg {Number} maxWidth
30772         * The maximum width of the quick tip (defaults to 300)
30773         */
30774        maxWidth : 300,
30775         /**
30776         * @cfg {Boolean} interceptTitles
30777         * True to automatically use the element's DOM title value if available (defaults to false)
30778         */
30779        interceptTitles : false,
30780         /**
30781         * @cfg {Boolean} trackMouse
30782         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30783         */
30784        trackMouse : false,
30785         /**
30786         * @cfg {Boolean} hideOnClick
30787         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30788         */
30789        hideOnClick : true,
30790         /**
30791         * @cfg {Number} showDelay
30792         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30793         */
30794        showDelay : 500,
30795         /**
30796         * @cfg {Number} hideDelay
30797         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30798         */
30799        hideDelay : 200,
30800         /**
30801         * @cfg {Boolean} autoHide
30802         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30803         * Used in conjunction with hideDelay.
30804         */
30805        autoHide : true,
30806         /**
30807         * @cfg {Boolean}
30808         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30809         * (defaults to true).  Used in conjunction with autoDismissDelay.
30810         */
30811        autoDismiss : true,
30812         /**
30813         * @cfg {Number}
30814         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30815         */
30816        autoDismissDelay : 5000,
30817        /**
30818         * @cfg {Boolean} animate
30819         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30820         */
30821        animate : false,
30822
30823        /**
30824         * @cfg {String} title
30825         * Title text to display (defaults to '').  This can be any valid HTML markup.
30826         */
30827         title: '',
30828        /**
30829         * @cfg {String} text
30830         * Body text to display (defaults to '').  This can be any valid HTML markup.
30831         */
30832         text : '',
30833        /**
30834         * @cfg {String} cls
30835         * A CSS class to apply to the base quick tip element (defaults to '').
30836         */
30837         cls : '',
30838        /**
30839         * @cfg {Number} width
30840         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30841         * minWidth or maxWidth.
30842         */
30843         width : null,
30844
30845     /**
30846      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30847      * or display QuickTips in a page.
30848      */
30849        init : function(){
30850           tm = Roo.QuickTips;
30851           cfg = tm.tagConfig;
30852           if(!inited){
30853               if(!Roo.isReady){ // allow calling of init() before onReady
30854                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30855                   return;
30856               }
30857               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30858               el.fxDefaults = {stopFx: true};
30859               // maximum custom styling
30860               //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>');
30861               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>');              
30862               tipTitle = el.child('h3');
30863               tipTitle.enableDisplayMode("block");
30864               tipBody = el.child('div.x-tip-bd');
30865               tipBodyText = el.child('div.x-tip-bd-inner');
30866               //bdLeft = el.child('div.x-tip-bd-left');
30867               //bdRight = el.child('div.x-tip-bd-right');
30868               close = el.child('div.x-tip-close');
30869               close.enableDisplayMode("block");
30870               close.on("click", hide);
30871               var d = Roo.get(document);
30872               d.on("mousedown", onDown);
30873               d.on("mouseover", onOver);
30874               d.on("mouseout", onOut);
30875               d.on("mousemove", onMove);
30876               esc = d.addKeyListener(27, hide);
30877               esc.disable();
30878               if(Roo.dd.DD){
30879                   dd = el.initDD("default", null, {
30880                       onDrag : function(){
30881                           el.sync();  
30882                       }
30883                   });
30884                   dd.setHandleElId(tipTitle.id);
30885                   dd.lock();
30886               }
30887               inited = true;
30888           }
30889           this.enable(); 
30890        },
30891
30892     /**
30893      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30894      * are supported:
30895      * <pre>
30896 Property    Type                   Description
30897 ----------  ---------------------  ------------------------------------------------------------------------
30898 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30899      * </ul>
30900      * @param {Object} config The config object
30901      */
30902        register : function(config){
30903            var cs = config instanceof Array ? config : arguments;
30904            for(var i = 0, len = cs.length; i < len; i++) {
30905                var c = cs[i];
30906                var target = c.target;
30907                if(target){
30908                    if(target instanceof Array){
30909                        for(var j = 0, jlen = target.length; j < jlen; j++){
30910                            tagEls[target[j]] = c;
30911                        }
30912                    }else{
30913                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30914                    }
30915                }
30916            }
30917        },
30918
30919     /**
30920      * Removes this quick tip from its element and destroys it.
30921      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30922      */
30923        unregister : function(el){
30924            delete tagEls[Roo.id(el)];
30925        },
30926
30927     /**
30928      * Enable this quick tip.
30929      */
30930        enable : function(){
30931            if(inited && disabled){
30932                locks.pop();
30933                if(locks.length < 1){
30934                    disabled = false;
30935                }
30936            }
30937        },
30938
30939     /**
30940      * Disable this quick tip.
30941      */
30942        disable : function(){
30943           disabled = true;
30944           clearTimeout(showProc);
30945           clearTimeout(hideProc);
30946           clearTimeout(dismissProc);
30947           if(ce){
30948               hide(true);
30949           }
30950           locks.push(1);
30951        },
30952
30953     /**
30954      * Returns true if the quick tip is enabled, else false.
30955      */
30956        isEnabled : function(){
30957             return !disabled;
30958        },
30959
30960         // private
30961        tagConfig : {
30962            namespace : "ext",
30963            attribute : "qtip",
30964            width : "width",
30965            target : "target",
30966            title : "qtitle",
30967            hide : "hide",
30968            cls : "qclass"
30969        }
30970    };
30971 }();
30972
30973 // backwards compat
30974 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30975  * Based on:
30976  * Ext JS Library 1.1.1
30977  * Copyright(c) 2006-2007, Ext JS, LLC.
30978  *
30979  * Originally Released Under LGPL - original licence link has changed is not relivant.
30980  *
30981  * Fork - LGPL
30982  * <script type="text/javascript">
30983  */
30984  
30985
30986 /**
30987  * @class Roo.tree.TreePanel
30988  * @extends Roo.data.Tree
30989
30990  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30991  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30992  * @cfg {Boolean} enableDD true to enable drag and drop
30993  * @cfg {Boolean} enableDrag true to enable just drag
30994  * @cfg {Boolean} enableDrop true to enable just drop
30995  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30996  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30997  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30998  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30999  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31000  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31001  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31002  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31003  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31004  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31005  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31006  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31007  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31008  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31009  * @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>
31010  * @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>
31011  * 
31012  * @constructor
31013  * @param {String/HTMLElement/Element} el The container element
31014  * @param {Object} config
31015  */
31016 Roo.tree.TreePanel = function(el, config){
31017     var root = false;
31018     var loader = false;
31019     if (config.root) {
31020         root = config.root;
31021         delete config.root;
31022     }
31023     if (config.loader) {
31024         loader = config.loader;
31025         delete config.loader;
31026     }
31027     
31028     Roo.apply(this, config);
31029     Roo.tree.TreePanel.superclass.constructor.call(this);
31030     this.el = Roo.get(el);
31031     this.el.addClass('x-tree');
31032     //console.log(root);
31033     if (root) {
31034         this.setRootNode( Roo.factory(root, Roo.tree));
31035     }
31036     if (loader) {
31037         this.loader = Roo.factory(loader, Roo.tree);
31038     }
31039    /**
31040     * Read-only. The id of the container element becomes this TreePanel's id.
31041     */
31042     this.id = this.el.id;
31043     this.addEvents({
31044         /**
31045         * @event beforeload
31046         * Fires before a node is loaded, return false to cancel
31047         * @param {Node} node The node being loaded
31048         */
31049         "beforeload" : true,
31050         /**
31051         * @event load
31052         * Fires when a node is loaded
31053         * @param {Node} node The node that was loaded
31054         */
31055         "load" : true,
31056         /**
31057         * @event textchange
31058         * Fires when the text for a node is changed
31059         * @param {Node} node The node
31060         * @param {String} text The new text
31061         * @param {String} oldText The old text
31062         */
31063         "textchange" : true,
31064         /**
31065         * @event beforeexpand
31066         * Fires before a node is expanded, return false to cancel.
31067         * @param {Node} node The node
31068         * @param {Boolean} deep
31069         * @param {Boolean} anim
31070         */
31071         "beforeexpand" : true,
31072         /**
31073         * @event beforecollapse
31074         * Fires before a node is collapsed, return false to cancel.
31075         * @param {Node} node The node
31076         * @param {Boolean} deep
31077         * @param {Boolean} anim
31078         */
31079         "beforecollapse" : true,
31080         /**
31081         * @event expand
31082         * Fires when a node is expanded
31083         * @param {Node} node The node
31084         */
31085         "expand" : true,
31086         /**
31087         * @event disabledchange
31088         * Fires when the disabled status of a node changes
31089         * @param {Node} node The node
31090         * @param {Boolean} disabled
31091         */
31092         "disabledchange" : true,
31093         /**
31094         * @event collapse
31095         * Fires when a node is collapsed
31096         * @param {Node} node The node
31097         */
31098         "collapse" : true,
31099         /**
31100         * @event beforeclick
31101         * Fires before click processing on a node. Return false to cancel the default action.
31102         * @param {Node} node The node
31103         * @param {Roo.EventObject} e The event object
31104         */
31105         "beforeclick":true,
31106         /**
31107         * @event checkchange
31108         * Fires when a node with a checkbox's checked property changes
31109         * @param {Node} this This node
31110         * @param {Boolean} checked
31111         */
31112         "checkchange":true,
31113         /**
31114         * @event click
31115         * Fires when a node is clicked
31116         * @param {Node} node The node
31117         * @param {Roo.EventObject} e The event object
31118         */
31119         "click":true,
31120         /**
31121         * @event dblclick
31122         * Fires when a node is double clicked
31123         * @param {Node} node The node
31124         * @param {Roo.EventObject} e The event object
31125         */
31126         "dblclick":true,
31127         /**
31128         * @event contextmenu
31129         * Fires when a node is right clicked
31130         * @param {Node} node The node
31131         * @param {Roo.EventObject} e The event object
31132         */
31133         "contextmenu":true,
31134         /**
31135         * @event beforechildrenrendered
31136         * Fires right before the child nodes for a node are rendered
31137         * @param {Node} node The node
31138         */
31139         "beforechildrenrendered":true,
31140         /**
31141         * @event startdrag
31142         * Fires when a node starts being dragged
31143         * @param {Roo.tree.TreePanel} this
31144         * @param {Roo.tree.TreeNode} node
31145         * @param {event} e The raw browser event
31146         */ 
31147        "startdrag" : true,
31148        /**
31149         * @event enddrag
31150         * Fires when a drag operation is complete
31151         * @param {Roo.tree.TreePanel} this
31152         * @param {Roo.tree.TreeNode} node
31153         * @param {event} e The raw browser event
31154         */
31155        "enddrag" : true,
31156        /**
31157         * @event dragdrop
31158         * Fires when a dragged node is dropped on a valid DD target
31159         * @param {Roo.tree.TreePanel} this
31160         * @param {Roo.tree.TreeNode} node
31161         * @param {DD} dd The dd it was dropped on
31162         * @param {event} e The raw browser event
31163         */
31164        "dragdrop" : true,
31165        /**
31166         * @event beforenodedrop
31167         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31168         * passed to handlers has the following properties:<br />
31169         * <ul style="padding:5px;padding-left:16px;">
31170         * <li>tree - The TreePanel</li>
31171         * <li>target - The node being targeted for the drop</li>
31172         * <li>data - The drag data from the drag source</li>
31173         * <li>point - The point of the drop - append, above or below</li>
31174         * <li>source - The drag source</li>
31175         * <li>rawEvent - Raw mouse event</li>
31176         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31177         * to be inserted by setting them on this object.</li>
31178         * <li>cancel - Set this to true to cancel the drop.</li>
31179         * </ul>
31180         * @param {Object} dropEvent
31181         */
31182        "beforenodedrop" : true,
31183        /**
31184         * @event nodedrop
31185         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31186         * passed to handlers has the following properties:<br />
31187         * <ul style="padding:5px;padding-left:16px;">
31188         * <li>tree - The TreePanel</li>
31189         * <li>target - The node being targeted for the drop</li>
31190         * <li>data - The drag data from the drag source</li>
31191         * <li>point - The point of the drop - append, above or below</li>
31192         * <li>source - The drag source</li>
31193         * <li>rawEvent - Raw mouse event</li>
31194         * <li>dropNode - Dropped node(s).</li>
31195         * </ul>
31196         * @param {Object} dropEvent
31197         */
31198        "nodedrop" : true,
31199         /**
31200         * @event nodedragover
31201         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31202         * passed to handlers has the following properties:<br />
31203         * <ul style="padding:5px;padding-left:16px;">
31204         * <li>tree - The TreePanel</li>
31205         * <li>target - The node being targeted for the drop</li>
31206         * <li>data - The drag data from the drag source</li>
31207         * <li>point - The point of the drop - append, above or below</li>
31208         * <li>source - The drag source</li>
31209         * <li>rawEvent - Raw mouse event</li>
31210         * <li>dropNode - Drop node(s) provided by the source.</li>
31211         * <li>cancel - Set this to true to signal drop not allowed.</li>
31212         * </ul>
31213         * @param {Object} dragOverEvent
31214         */
31215        "nodedragover" : true
31216         
31217     });
31218     if(this.singleExpand){
31219        this.on("beforeexpand", this.restrictExpand, this);
31220     }
31221     if (this.editor) {
31222         this.editor.tree = this;
31223         this.editor = Roo.factory(this.editor, Roo.tree);
31224     }
31225     
31226     if (this.selModel) {
31227         this.selModel = Roo.factory(this.selModel, Roo.tree);
31228     }
31229    
31230 };
31231 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31232     rootVisible : true,
31233     animate: Roo.enableFx,
31234     lines : true,
31235     enableDD : false,
31236     hlDrop : Roo.enableFx,
31237   
31238     renderer: false,
31239     
31240     rendererTip: false,
31241     // private
31242     restrictExpand : function(node){
31243         var p = node.parentNode;
31244         if(p){
31245             if(p.expandedChild && p.expandedChild.parentNode == p){
31246                 p.expandedChild.collapse();
31247             }
31248             p.expandedChild = node;
31249         }
31250     },
31251
31252     // private override
31253     setRootNode : function(node){
31254         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31255         if(!this.rootVisible){
31256             node.ui = new Roo.tree.RootTreeNodeUI(node);
31257         }
31258         return node;
31259     },
31260
31261     /**
31262      * Returns the container element for this TreePanel
31263      */
31264     getEl : function(){
31265         return this.el;
31266     },
31267
31268     /**
31269      * Returns the default TreeLoader for this TreePanel
31270      */
31271     getLoader : function(){
31272         return this.loader;
31273     },
31274
31275     /**
31276      * Expand all nodes
31277      */
31278     expandAll : function(){
31279         this.root.expand(true);
31280     },
31281
31282     /**
31283      * Collapse all nodes
31284      */
31285     collapseAll : function(){
31286         this.root.collapse(true);
31287     },
31288
31289     /**
31290      * Returns the selection model used by this TreePanel
31291      */
31292     getSelectionModel : function(){
31293         if(!this.selModel){
31294             this.selModel = new Roo.tree.DefaultSelectionModel();
31295         }
31296         return this.selModel;
31297     },
31298
31299     /**
31300      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31301      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31302      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31303      * @return {Array}
31304      */
31305     getChecked : function(a, startNode){
31306         startNode = startNode || this.root;
31307         var r = [];
31308         var f = function(){
31309             if(this.attributes.checked){
31310                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31311             }
31312         }
31313         startNode.cascade(f);
31314         return r;
31315     },
31316
31317     /**
31318      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31319      * @param {String} path
31320      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31321      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31322      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31323      */
31324     expandPath : function(path, attr, callback){
31325         attr = attr || "id";
31326         var keys = path.split(this.pathSeparator);
31327         var curNode = this.root;
31328         if(curNode.attributes[attr] != keys[1]){ // invalid root
31329             if(callback){
31330                 callback(false, null);
31331             }
31332             return;
31333         }
31334         var index = 1;
31335         var f = function(){
31336             if(++index == keys.length){
31337                 if(callback){
31338                     callback(true, curNode);
31339                 }
31340                 return;
31341             }
31342             var c = curNode.findChild(attr, keys[index]);
31343             if(!c){
31344                 if(callback){
31345                     callback(false, curNode);
31346                 }
31347                 return;
31348             }
31349             curNode = c;
31350             c.expand(false, false, f);
31351         };
31352         curNode.expand(false, false, f);
31353     },
31354
31355     /**
31356      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31357      * @param {String} path
31358      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31359      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31360      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31361      */
31362     selectPath : function(path, attr, callback){
31363         attr = attr || "id";
31364         var keys = path.split(this.pathSeparator);
31365         var v = keys.pop();
31366         if(keys.length > 0){
31367             var f = function(success, node){
31368                 if(success && node){
31369                     var n = node.findChild(attr, v);
31370                     if(n){
31371                         n.select();
31372                         if(callback){
31373                             callback(true, n);
31374                         }
31375                     }else if(callback){
31376                         callback(false, n);
31377                     }
31378                 }else{
31379                     if(callback){
31380                         callback(false, n);
31381                     }
31382                 }
31383             };
31384             this.expandPath(keys.join(this.pathSeparator), attr, f);
31385         }else{
31386             this.root.select();
31387             if(callback){
31388                 callback(true, this.root);
31389             }
31390         }
31391     },
31392
31393     getTreeEl : function(){
31394         return this.el;
31395     },
31396
31397     /**
31398      * Trigger rendering of this TreePanel
31399      */
31400     render : function(){
31401         if (this.innerCt) {
31402             return this; // stop it rendering more than once!!
31403         }
31404         
31405         this.innerCt = this.el.createChild({tag:"ul",
31406                cls:"x-tree-root-ct " +
31407                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31408
31409         if(this.containerScroll){
31410             Roo.dd.ScrollManager.register(this.el);
31411         }
31412         if((this.enableDD || this.enableDrop) && !this.dropZone){
31413            /**
31414             * The dropZone used by this tree if drop is enabled
31415             * @type Roo.tree.TreeDropZone
31416             */
31417              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31418                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31419            });
31420         }
31421         if((this.enableDD || this.enableDrag) && !this.dragZone){
31422            /**
31423             * The dragZone used by this tree if drag is enabled
31424             * @type Roo.tree.TreeDragZone
31425             */
31426             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31427                ddGroup: this.ddGroup || "TreeDD",
31428                scroll: this.ddScroll
31429            });
31430         }
31431         this.getSelectionModel().init(this);
31432         if (!this.root) {
31433             console.log("ROOT not set in tree");
31434             return;
31435         }
31436         this.root.render();
31437         if(!this.rootVisible){
31438             this.root.renderChildren();
31439         }
31440         return this;
31441     }
31442 });/*
31443  * Based on:
31444  * Ext JS Library 1.1.1
31445  * Copyright(c) 2006-2007, Ext JS, LLC.
31446  *
31447  * Originally Released Under LGPL - original licence link has changed is not relivant.
31448  *
31449  * Fork - LGPL
31450  * <script type="text/javascript">
31451  */
31452  
31453
31454 /**
31455  * @class Roo.tree.DefaultSelectionModel
31456  * @extends Roo.util.Observable
31457  * The default single selection for a TreePanel.
31458  * @param {Object} cfg Configuration
31459  */
31460 Roo.tree.DefaultSelectionModel = function(cfg){
31461    this.selNode = null;
31462    
31463    
31464    
31465    this.addEvents({
31466        /**
31467         * @event selectionchange
31468         * Fires when the selected node changes
31469         * @param {DefaultSelectionModel} this
31470         * @param {TreeNode} node the new selection
31471         */
31472        "selectionchange" : true,
31473
31474        /**
31475         * @event beforeselect
31476         * Fires before the selected node changes, return false to cancel the change
31477         * @param {DefaultSelectionModel} this
31478         * @param {TreeNode} node the new selection
31479         * @param {TreeNode} node the old selection
31480         */
31481        "beforeselect" : true
31482    });
31483    
31484     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31485 };
31486
31487 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31488     init : function(tree){
31489         this.tree = tree;
31490         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31491         tree.on("click", this.onNodeClick, this);
31492     },
31493     
31494     onNodeClick : function(node, e){
31495         if (e.ctrlKey && this.selNode == node)  {
31496             this.unselect(node);
31497             return;
31498         }
31499         this.select(node);
31500     },
31501     
31502     /**
31503      * Select a node.
31504      * @param {TreeNode} node The node to select
31505      * @return {TreeNode} The selected node
31506      */
31507     select : function(node){
31508         var last = this.selNode;
31509         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31510             if(last){
31511                 last.ui.onSelectedChange(false);
31512             }
31513             this.selNode = node;
31514             node.ui.onSelectedChange(true);
31515             this.fireEvent("selectionchange", this, node, last);
31516         }
31517         return node;
31518     },
31519     
31520     /**
31521      * Deselect a node.
31522      * @param {TreeNode} node The node to unselect
31523      */
31524     unselect : function(node){
31525         if(this.selNode == node){
31526             this.clearSelections();
31527         }    
31528     },
31529     
31530     /**
31531      * Clear all selections
31532      */
31533     clearSelections : function(){
31534         var n = this.selNode;
31535         if(n){
31536             n.ui.onSelectedChange(false);
31537             this.selNode = null;
31538             this.fireEvent("selectionchange", this, null);
31539         }
31540         return n;
31541     },
31542     
31543     /**
31544      * Get the selected node
31545      * @return {TreeNode} The selected node
31546      */
31547     getSelectedNode : function(){
31548         return this.selNode;    
31549     },
31550     
31551     /**
31552      * Returns true if the node is selected
31553      * @param {TreeNode} node The node to check
31554      * @return {Boolean}
31555      */
31556     isSelected : function(node){
31557         return this.selNode == node;  
31558     },
31559
31560     /**
31561      * Selects the node above the selected node in the tree, intelligently walking the nodes
31562      * @return TreeNode The new selection
31563      */
31564     selectPrevious : function(){
31565         var s = this.selNode || this.lastSelNode;
31566         if(!s){
31567             return null;
31568         }
31569         var ps = s.previousSibling;
31570         if(ps){
31571             if(!ps.isExpanded() || ps.childNodes.length < 1){
31572                 return this.select(ps);
31573             } else{
31574                 var lc = ps.lastChild;
31575                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31576                     lc = lc.lastChild;
31577                 }
31578                 return this.select(lc);
31579             }
31580         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31581             return this.select(s.parentNode);
31582         }
31583         return null;
31584     },
31585
31586     /**
31587      * Selects the node above the selected node in the tree, intelligently walking the nodes
31588      * @return TreeNode The new selection
31589      */
31590     selectNext : function(){
31591         var s = this.selNode || this.lastSelNode;
31592         if(!s){
31593             return null;
31594         }
31595         if(s.firstChild && s.isExpanded()){
31596              return this.select(s.firstChild);
31597          }else if(s.nextSibling){
31598              return this.select(s.nextSibling);
31599          }else if(s.parentNode){
31600             var newS = null;
31601             s.parentNode.bubble(function(){
31602                 if(this.nextSibling){
31603                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31604                     return false;
31605                 }
31606             });
31607             return newS;
31608          }
31609         return null;
31610     },
31611
31612     onKeyDown : function(e){
31613         var s = this.selNode || this.lastSelNode;
31614         // undesirable, but required
31615         var sm = this;
31616         if(!s){
31617             return;
31618         }
31619         var k = e.getKey();
31620         switch(k){
31621              case e.DOWN:
31622                  e.stopEvent();
31623                  this.selectNext();
31624              break;
31625              case e.UP:
31626                  e.stopEvent();
31627                  this.selectPrevious();
31628              break;
31629              case e.RIGHT:
31630                  e.preventDefault();
31631                  if(s.hasChildNodes()){
31632                      if(!s.isExpanded()){
31633                          s.expand();
31634                      }else if(s.firstChild){
31635                          this.select(s.firstChild, e);
31636                      }
31637                  }
31638              break;
31639              case e.LEFT:
31640                  e.preventDefault();
31641                  if(s.hasChildNodes() && s.isExpanded()){
31642                      s.collapse();
31643                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31644                      this.select(s.parentNode, e);
31645                  }
31646              break;
31647         };
31648     }
31649 });
31650
31651 /**
31652  * @class Roo.tree.MultiSelectionModel
31653  * @extends Roo.util.Observable
31654  * Multi selection for a TreePanel.
31655  * @param {Object} cfg Configuration
31656  */
31657 Roo.tree.MultiSelectionModel = function(){
31658    this.selNodes = [];
31659    this.selMap = {};
31660    this.addEvents({
31661        /**
31662         * @event selectionchange
31663         * Fires when the selected nodes change
31664         * @param {MultiSelectionModel} this
31665         * @param {Array} nodes Array of the selected nodes
31666         */
31667        "selectionchange" : true
31668    });
31669    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31670    
31671 };
31672
31673 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31674     init : function(tree){
31675         this.tree = tree;
31676         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31677         tree.on("click", this.onNodeClick, this);
31678     },
31679     
31680     onNodeClick : function(node, e){
31681         this.select(node, e, e.ctrlKey);
31682     },
31683     
31684     /**
31685      * Select a node.
31686      * @param {TreeNode} node The node to select
31687      * @param {EventObject} e (optional) An event associated with the selection
31688      * @param {Boolean} keepExisting True to retain existing selections
31689      * @return {TreeNode} The selected node
31690      */
31691     select : function(node, e, keepExisting){
31692         if(keepExisting !== true){
31693             this.clearSelections(true);
31694         }
31695         if(this.isSelected(node)){
31696             this.lastSelNode = node;
31697             return node;
31698         }
31699         this.selNodes.push(node);
31700         this.selMap[node.id] = node;
31701         this.lastSelNode = node;
31702         node.ui.onSelectedChange(true);
31703         this.fireEvent("selectionchange", this, this.selNodes);
31704         return node;
31705     },
31706     
31707     /**
31708      * Deselect a node.
31709      * @param {TreeNode} node The node to unselect
31710      */
31711     unselect : function(node){
31712         if(this.selMap[node.id]){
31713             node.ui.onSelectedChange(false);
31714             var sn = this.selNodes;
31715             var index = -1;
31716             if(sn.indexOf){
31717                 index = sn.indexOf(node);
31718             }else{
31719                 for(var i = 0, len = sn.length; i < len; i++){
31720                     if(sn[i] == node){
31721                         index = i;
31722                         break;
31723                     }
31724                 }
31725             }
31726             if(index != -1){
31727                 this.selNodes.splice(index, 1);
31728             }
31729             delete this.selMap[node.id];
31730             this.fireEvent("selectionchange", this, this.selNodes);
31731         }
31732     },
31733     
31734     /**
31735      * Clear all selections
31736      */
31737     clearSelections : function(suppressEvent){
31738         var sn = this.selNodes;
31739         if(sn.length > 0){
31740             for(var i = 0, len = sn.length; i < len; i++){
31741                 sn[i].ui.onSelectedChange(false);
31742             }
31743             this.selNodes = [];
31744             this.selMap = {};
31745             if(suppressEvent !== true){
31746                 this.fireEvent("selectionchange", this, this.selNodes);
31747             }
31748         }
31749     },
31750     
31751     /**
31752      * Returns true if the node is selected
31753      * @param {TreeNode} node The node to check
31754      * @return {Boolean}
31755      */
31756     isSelected : function(node){
31757         return this.selMap[node.id] ? true : false;  
31758     },
31759     
31760     /**
31761      * Returns an array of the selected nodes
31762      * @return {Array}
31763      */
31764     getSelectedNodes : function(){
31765         return this.selNodes;    
31766     },
31767
31768     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31769
31770     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31771
31772     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31773 });/*
31774  * Based on:
31775  * Ext JS Library 1.1.1
31776  * Copyright(c) 2006-2007, Ext JS, LLC.
31777  *
31778  * Originally Released Under LGPL - original licence link has changed is not relivant.
31779  *
31780  * Fork - LGPL
31781  * <script type="text/javascript">
31782  */
31783  
31784 /**
31785  * @class Roo.tree.TreeNode
31786  * @extends Roo.data.Node
31787  * @cfg {String} text The text for this node
31788  * @cfg {Boolean} expanded true to start the node expanded
31789  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31790  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31791  * @cfg {Boolean} disabled true to start the node disabled
31792  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31793  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31794  * @cfg {String} cls A css class to be added to the node
31795  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31796  * @cfg {String} href URL of the link used for the node (defaults to #)
31797  * @cfg {String} hrefTarget target frame for the link
31798  * @cfg {String} qtip An Ext QuickTip for the node
31799  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31800  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31801  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31802  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31803  * (defaults to undefined with no checkbox rendered)
31804  * @constructor
31805  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31806  */
31807 Roo.tree.TreeNode = function(attributes){
31808     attributes = attributes || {};
31809     if(typeof attributes == "string"){
31810         attributes = {text: attributes};
31811     }
31812     this.childrenRendered = false;
31813     this.rendered = false;
31814     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31815     this.expanded = attributes.expanded === true;
31816     this.isTarget = attributes.isTarget !== false;
31817     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31818     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31819
31820     /**
31821      * Read-only. The text for this node. To change it use setText().
31822      * @type String
31823      */
31824     this.text = attributes.text;
31825     /**
31826      * True if this node is disabled.
31827      * @type Boolean
31828      */
31829     this.disabled = attributes.disabled === true;
31830
31831     this.addEvents({
31832         /**
31833         * @event textchange
31834         * Fires when the text for this node is changed
31835         * @param {Node} this This node
31836         * @param {String} text The new text
31837         * @param {String} oldText The old text
31838         */
31839         "textchange" : true,
31840         /**
31841         * @event beforeexpand
31842         * Fires before this node is expanded, return false to cancel.
31843         * @param {Node} this This node
31844         * @param {Boolean} deep
31845         * @param {Boolean} anim
31846         */
31847         "beforeexpand" : true,
31848         /**
31849         * @event beforecollapse
31850         * Fires before this node is collapsed, return false to cancel.
31851         * @param {Node} this This node
31852         * @param {Boolean} deep
31853         * @param {Boolean} anim
31854         */
31855         "beforecollapse" : true,
31856         /**
31857         * @event expand
31858         * Fires when this node is expanded
31859         * @param {Node} this This node
31860         */
31861         "expand" : true,
31862         /**
31863         * @event disabledchange
31864         * Fires when the disabled status of this node changes
31865         * @param {Node} this This node
31866         * @param {Boolean} disabled
31867         */
31868         "disabledchange" : true,
31869         /**
31870         * @event collapse
31871         * Fires when this node is collapsed
31872         * @param {Node} this This node
31873         */
31874         "collapse" : true,
31875         /**
31876         * @event beforeclick
31877         * Fires before click processing. Return false to cancel the default action.
31878         * @param {Node} this This node
31879         * @param {Roo.EventObject} e The event object
31880         */
31881         "beforeclick":true,
31882         /**
31883         * @event checkchange
31884         * Fires when a node with a checkbox's checked property changes
31885         * @param {Node} this This node
31886         * @param {Boolean} checked
31887         */
31888         "checkchange":true,
31889         /**
31890         * @event click
31891         * Fires when this node is clicked
31892         * @param {Node} this This node
31893         * @param {Roo.EventObject} e The event object
31894         */
31895         "click":true,
31896         /**
31897         * @event dblclick
31898         * Fires when this node is double clicked
31899         * @param {Node} this This node
31900         * @param {Roo.EventObject} e The event object
31901         */
31902         "dblclick":true,
31903         /**
31904         * @event contextmenu
31905         * Fires when this node is right clicked
31906         * @param {Node} this This node
31907         * @param {Roo.EventObject} e The event object
31908         */
31909         "contextmenu":true,
31910         /**
31911         * @event beforechildrenrendered
31912         * Fires right before the child nodes for this node are rendered
31913         * @param {Node} this This node
31914         */
31915         "beforechildrenrendered":true
31916     });
31917
31918     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31919
31920     /**
31921      * Read-only. The UI for this node
31922      * @type TreeNodeUI
31923      */
31924     this.ui = new uiClass(this);
31925     
31926     // finally support items[]
31927     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31928         return;
31929     }
31930     
31931     
31932     Roo.each(this.attributes.items, function(c) {
31933         this.appendChild(Roo.factory(c,Roo.Tree));
31934     }, this);
31935     delete this.attributes.items;
31936     
31937     
31938     
31939 };
31940 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31941     preventHScroll: true,
31942     /**
31943      * Returns true if this node is expanded
31944      * @return {Boolean}
31945      */
31946     isExpanded : function(){
31947         return this.expanded;
31948     },
31949
31950     /**
31951      * Returns the UI object for this node
31952      * @return {TreeNodeUI}
31953      */
31954     getUI : function(){
31955         return this.ui;
31956     },
31957
31958     // private override
31959     setFirstChild : function(node){
31960         var of = this.firstChild;
31961         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31962         if(this.childrenRendered && of && node != of){
31963             of.renderIndent(true, true);
31964         }
31965         if(this.rendered){
31966             this.renderIndent(true, true);
31967         }
31968     },
31969
31970     // private override
31971     setLastChild : function(node){
31972         var ol = this.lastChild;
31973         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31974         if(this.childrenRendered && ol && node != ol){
31975             ol.renderIndent(true, true);
31976         }
31977         if(this.rendered){
31978             this.renderIndent(true, true);
31979         }
31980     },
31981
31982     // these methods are overridden to provide lazy rendering support
31983     // private override
31984     appendChild : function()
31985     {
31986         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31987         if(node && this.childrenRendered){
31988             node.render();
31989         }
31990         this.ui.updateExpandIcon();
31991         return node;
31992     },
31993
31994     // private override
31995     removeChild : function(node){
31996         this.ownerTree.getSelectionModel().unselect(node);
31997         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31998         // if it's been rendered remove dom node
31999         if(this.childrenRendered){
32000             node.ui.remove();
32001         }
32002         if(this.childNodes.length < 1){
32003             this.collapse(false, false);
32004         }else{
32005             this.ui.updateExpandIcon();
32006         }
32007         if(!this.firstChild) {
32008             this.childrenRendered = false;
32009         }
32010         return node;
32011     },
32012
32013     // private override
32014     insertBefore : function(node, refNode){
32015         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32016         if(newNode && refNode && this.childrenRendered){
32017             node.render();
32018         }
32019         this.ui.updateExpandIcon();
32020         return newNode;
32021     },
32022
32023     /**
32024      * Sets the text for this node
32025      * @param {String} text
32026      */
32027     setText : function(text){
32028         var oldText = this.text;
32029         this.text = text;
32030         this.attributes.text = text;
32031         if(this.rendered){ // event without subscribing
32032             this.ui.onTextChange(this, text, oldText);
32033         }
32034         this.fireEvent("textchange", this, text, oldText);
32035     },
32036
32037     /**
32038      * Triggers selection of this node
32039      */
32040     select : function(){
32041         this.getOwnerTree().getSelectionModel().select(this);
32042     },
32043
32044     /**
32045      * Triggers deselection of this node
32046      */
32047     unselect : function(){
32048         this.getOwnerTree().getSelectionModel().unselect(this);
32049     },
32050
32051     /**
32052      * Returns true if this node is selected
32053      * @return {Boolean}
32054      */
32055     isSelected : function(){
32056         return this.getOwnerTree().getSelectionModel().isSelected(this);
32057     },
32058
32059     /**
32060      * Expand this node.
32061      * @param {Boolean} deep (optional) True to expand all children as well
32062      * @param {Boolean} anim (optional) false to cancel the default animation
32063      * @param {Function} callback (optional) A callback to be called when
32064      * expanding this node completes (does not wait for deep expand to complete).
32065      * Called with 1 parameter, this node.
32066      */
32067     expand : function(deep, anim, callback){
32068         if(!this.expanded){
32069             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32070                 return;
32071             }
32072             if(!this.childrenRendered){
32073                 this.renderChildren();
32074             }
32075             this.expanded = true;
32076             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32077                 this.ui.animExpand(function(){
32078                     this.fireEvent("expand", this);
32079                     if(typeof callback == "function"){
32080                         callback(this);
32081                     }
32082                     if(deep === true){
32083                         this.expandChildNodes(true);
32084                     }
32085                 }.createDelegate(this));
32086                 return;
32087             }else{
32088                 this.ui.expand();
32089                 this.fireEvent("expand", this);
32090                 if(typeof callback == "function"){
32091                     callback(this);
32092                 }
32093             }
32094         }else{
32095            if(typeof callback == "function"){
32096                callback(this);
32097            }
32098         }
32099         if(deep === true){
32100             this.expandChildNodes(true);
32101         }
32102     },
32103
32104     isHiddenRoot : function(){
32105         return this.isRoot && !this.getOwnerTree().rootVisible;
32106     },
32107
32108     /**
32109      * Collapse this node.
32110      * @param {Boolean} deep (optional) True to collapse all children as well
32111      * @param {Boolean} anim (optional) false to cancel the default animation
32112      */
32113     collapse : function(deep, anim){
32114         if(this.expanded && !this.isHiddenRoot()){
32115             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32116                 return;
32117             }
32118             this.expanded = false;
32119             if((this.getOwnerTree().animate && anim !== false) || anim){
32120                 this.ui.animCollapse(function(){
32121                     this.fireEvent("collapse", this);
32122                     if(deep === true){
32123                         this.collapseChildNodes(true);
32124                     }
32125                 }.createDelegate(this));
32126                 return;
32127             }else{
32128                 this.ui.collapse();
32129                 this.fireEvent("collapse", this);
32130             }
32131         }
32132         if(deep === true){
32133             var cs = this.childNodes;
32134             for(var i = 0, len = cs.length; i < len; i++) {
32135                 cs[i].collapse(true, false);
32136             }
32137         }
32138     },
32139
32140     // private
32141     delayedExpand : function(delay){
32142         if(!this.expandProcId){
32143             this.expandProcId = this.expand.defer(delay, this);
32144         }
32145     },
32146
32147     // private
32148     cancelExpand : function(){
32149         if(this.expandProcId){
32150             clearTimeout(this.expandProcId);
32151         }
32152         this.expandProcId = false;
32153     },
32154
32155     /**
32156      * Toggles expanded/collapsed state of the node
32157      */
32158     toggle : function(){
32159         if(this.expanded){
32160             this.collapse();
32161         }else{
32162             this.expand();
32163         }
32164     },
32165
32166     /**
32167      * Ensures all parent nodes are expanded
32168      */
32169     ensureVisible : function(callback){
32170         var tree = this.getOwnerTree();
32171         tree.expandPath(this.parentNode.getPath(), false, function(){
32172             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32173             Roo.callback(callback);
32174         }.createDelegate(this));
32175     },
32176
32177     /**
32178      * Expand all child nodes
32179      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32180      */
32181     expandChildNodes : function(deep){
32182         var cs = this.childNodes;
32183         for(var i = 0, len = cs.length; i < len; i++) {
32184                 cs[i].expand(deep);
32185         }
32186     },
32187
32188     /**
32189      * Collapse all child nodes
32190      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32191      */
32192     collapseChildNodes : function(deep){
32193         var cs = this.childNodes;
32194         for(var i = 0, len = cs.length; i < len; i++) {
32195                 cs[i].collapse(deep);
32196         }
32197     },
32198
32199     /**
32200      * Disables this node
32201      */
32202     disable : function(){
32203         this.disabled = true;
32204         this.unselect();
32205         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32206             this.ui.onDisableChange(this, true);
32207         }
32208         this.fireEvent("disabledchange", this, true);
32209     },
32210
32211     /**
32212      * Enables this node
32213      */
32214     enable : function(){
32215         this.disabled = false;
32216         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32217             this.ui.onDisableChange(this, false);
32218         }
32219         this.fireEvent("disabledchange", this, false);
32220     },
32221
32222     // private
32223     renderChildren : function(suppressEvent){
32224         if(suppressEvent !== false){
32225             this.fireEvent("beforechildrenrendered", this);
32226         }
32227         var cs = this.childNodes;
32228         for(var i = 0, len = cs.length; i < len; i++){
32229             cs[i].render(true);
32230         }
32231         this.childrenRendered = true;
32232     },
32233
32234     // private
32235     sort : function(fn, scope){
32236         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32237         if(this.childrenRendered){
32238             var cs = this.childNodes;
32239             for(var i = 0, len = cs.length; i < len; i++){
32240                 cs[i].render(true);
32241             }
32242         }
32243     },
32244
32245     // private
32246     render : function(bulkRender){
32247         this.ui.render(bulkRender);
32248         if(!this.rendered){
32249             this.rendered = true;
32250             if(this.expanded){
32251                 this.expanded = false;
32252                 this.expand(false, false);
32253             }
32254         }
32255     },
32256
32257     // private
32258     renderIndent : function(deep, refresh){
32259         if(refresh){
32260             this.ui.childIndent = null;
32261         }
32262         this.ui.renderIndent();
32263         if(deep === true && this.childrenRendered){
32264             var cs = this.childNodes;
32265             for(var i = 0, len = cs.length; i < len; i++){
32266                 cs[i].renderIndent(true, refresh);
32267             }
32268         }
32269     }
32270 });/*
32271  * Based on:
32272  * Ext JS Library 1.1.1
32273  * Copyright(c) 2006-2007, Ext JS, LLC.
32274  *
32275  * Originally Released Under LGPL - original licence link has changed is not relivant.
32276  *
32277  * Fork - LGPL
32278  * <script type="text/javascript">
32279  */
32280  
32281 /**
32282  * @class Roo.tree.AsyncTreeNode
32283  * @extends Roo.tree.TreeNode
32284  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32285  * @constructor
32286  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32287  */
32288  Roo.tree.AsyncTreeNode = function(config){
32289     this.loaded = false;
32290     this.loading = false;
32291     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32292     /**
32293     * @event beforeload
32294     * Fires before this node is loaded, return false to cancel
32295     * @param {Node} this This node
32296     */
32297     this.addEvents({'beforeload':true, 'load': true});
32298     /**
32299     * @event load
32300     * Fires when this node is loaded
32301     * @param {Node} this This node
32302     */
32303     /**
32304      * The loader used by this node (defaults to using the tree's defined loader)
32305      * @type TreeLoader
32306      * @property loader
32307      */
32308 };
32309 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32310     expand : function(deep, anim, callback){
32311         if(this.loading){ // if an async load is already running, waiting til it's done
32312             var timer;
32313             var f = function(){
32314                 if(!this.loading){ // done loading
32315                     clearInterval(timer);
32316                     this.expand(deep, anim, callback);
32317                 }
32318             }.createDelegate(this);
32319             timer = setInterval(f, 200);
32320             return;
32321         }
32322         if(!this.loaded){
32323             if(this.fireEvent("beforeload", this) === false){
32324                 return;
32325             }
32326             this.loading = true;
32327             this.ui.beforeLoad(this);
32328             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32329             if(loader){
32330                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32331                 return;
32332             }
32333         }
32334         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32335     },
32336     
32337     /**
32338      * Returns true if this node is currently loading
32339      * @return {Boolean}
32340      */
32341     isLoading : function(){
32342         return this.loading;  
32343     },
32344     
32345     loadComplete : function(deep, anim, callback){
32346         this.loading = false;
32347         this.loaded = true;
32348         this.ui.afterLoad(this);
32349         this.fireEvent("load", this);
32350         this.expand(deep, anim, callback);
32351     },
32352     
32353     /**
32354      * Returns true if this node has been loaded
32355      * @return {Boolean}
32356      */
32357     isLoaded : function(){
32358         return this.loaded;
32359     },
32360     
32361     hasChildNodes : function(){
32362         if(!this.isLeaf() && !this.loaded){
32363             return true;
32364         }else{
32365             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32366         }
32367     },
32368
32369     /**
32370      * Trigger a reload for this node
32371      * @param {Function} callback
32372      */
32373     reload : function(callback){
32374         this.collapse(false, false);
32375         while(this.firstChild){
32376             this.removeChild(this.firstChild);
32377         }
32378         this.childrenRendered = false;
32379         this.loaded = false;
32380         if(this.isHiddenRoot()){
32381             this.expanded = false;
32382         }
32383         this.expand(false, false, callback);
32384     }
32385 });/*
32386  * Based on:
32387  * Ext JS Library 1.1.1
32388  * Copyright(c) 2006-2007, Ext JS, LLC.
32389  *
32390  * Originally Released Under LGPL - original licence link has changed is not relivant.
32391  *
32392  * Fork - LGPL
32393  * <script type="text/javascript">
32394  */
32395  
32396 /**
32397  * @class Roo.tree.TreeNodeUI
32398  * @constructor
32399  * @param {Object} node The node to render
32400  * The TreeNode UI implementation is separate from the
32401  * tree implementation. Unless you are customizing the tree UI,
32402  * you should never have to use this directly.
32403  */
32404 Roo.tree.TreeNodeUI = function(node){
32405     this.node = node;
32406     this.rendered = false;
32407     this.animating = false;
32408     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32409 };
32410
32411 Roo.tree.TreeNodeUI.prototype = {
32412     removeChild : function(node){
32413         if(this.rendered){
32414             this.ctNode.removeChild(node.ui.getEl());
32415         }
32416     },
32417
32418     beforeLoad : function(){
32419          this.addClass("x-tree-node-loading");
32420     },
32421
32422     afterLoad : function(){
32423          this.removeClass("x-tree-node-loading");
32424     },
32425
32426     onTextChange : function(node, text, oldText){
32427         if(this.rendered){
32428             this.textNode.innerHTML = text;
32429         }
32430     },
32431
32432     onDisableChange : function(node, state){
32433         this.disabled = state;
32434         if(state){
32435             this.addClass("x-tree-node-disabled");
32436         }else{
32437             this.removeClass("x-tree-node-disabled");
32438         }
32439     },
32440
32441     onSelectedChange : function(state){
32442         if(state){
32443             this.focus();
32444             this.addClass("x-tree-selected");
32445         }else{
32446             //this.blur();
32447             this.removeClass("x-tree-selected");
32448         }
32449     },
32450
32451     onMove : function(tree, node, oldParent, newParent, index, refNode){
32452         this.childIndent = null;
32453         if(this.rendered){
32454             var targetNode = newParent.ui.getContainer();
32455             if(!targetNode){//target not rendered
32456                 this.holder = document.createElement("div");
32457                 this.holder.appendChild(this.wrap);
32458                 return;
32459             }
32460             var insertBefore = refNode ? refNode.ui.getEl() : null;
32461             if(insertBefore){
32462                 targetNode.insertBefore(this.wrap, insertBefore);
32463             }else{
32464                 targetNode.appendChild(this.wrap);
32465             }
32466             this.node.renderIndent(true);
32467         }
32468     },
32469
32470     addClass : function(cls){
32471         if(this.elNode){
32472             Roo.fly(this.elNode).addClass(cls);
32473         }
32474     },
32475
32476     removeClass : function(cls){
32477         if(this.elNode){
32478             Roo.fly(this.elNode).removeClass(cls);
32479         }
32480     },
32481
32482     remove : function(){
32483         if(this.rendered){
32484             this.holder = document.createElement("div");
32485             this.holder.appendChild(this.wrap);
32486         }
32487     },
32488
32489     fireEvent : function(){
32490         return this.node.fireEvent.apply(this.node, arguments);
32491     },
32492
32493     initEvents : function(){
32494         this.node.on("move", this.onMove, this);
32495         var E = Roo.EventManager;
32496         var a = this.anchor;
32497
32498         var el = Roo.fly(a, '_treeui');
32499
32500         if(Roo.isOpera){ // opera render bug ignores the CSS
32501             el.setStyle("text-decoration", "none");
32502         }
32503
32504         el.on("click", this.onClick, this);
32505         el.on("dblclick", this.onDblClick, this);
32506
32507         if(this.checkbox){
32508             Roo.EventManager.on(this.checkbox,
32509                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32510         }
32511
32512         el.on("contextmenu", this.onContextMenu, this);
32513
32514         var icon = Roo.fly(this.iconNode);
32515         icon.on("click", this.onClick, this);
32516         icon.on("dblclick", this.onDblClick, this);
32517         icon.on("contextmenu", this.onContextMenu, this);
32518         E.on(this.ecNode, "click", this.ecClick, this, true);
32519
32520         if(this.node.disabled){
32521             this.addClass("x-tree-node-disabled");
32522         }
32523         if(this.node.hidden){
32524             this.addClass("x-tree-node-disabled");
32525         }
32526         var ot = this.node.getOwnerTree();
32527         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32528         if(dd && (!this.node.isRoot || ot.rootVisible)){
32529             Roo.dd.Registry.register(this.elNode, {
32530                 node: this.node,
32531                 handles: this.getDDHandles(),
32532                 isHandle: false
32533             });
32534         }
32535     },
32536
32537     getDDHandles : function(){
32538         return [this.iconNode, this.textNode];
32539     },
32540
32541     hide : function(){
32542         if(this.rendered){
32543             this.wrap.style.display = "none";
32544         }
32545     },
32546
32547     show : function(){
32548         if(this.rendered){
32549             this.wrap.style.display = "";
32550         }
32551     },
32552
32553     onContextMenu : function(e){
32554         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32555             e.preventDefault();
32556             this.focus();
32557             this.fireEvent("contextmenu", this.node, e);
32558         }
32559     },
32560
32561     onClick : function(e){
32562         if(this.dropping){
32563             e.stopEvent();
32564             return;
32565         }
32566         if(this.fireEvent("beforeclick", this.node, e) !== false){
32567             if(!this.disabled && this.node.attributes.href){
32568                 this.fireEvent("click", this.node, e);
32569                 return;
32570             }
32571             e.preventDefault();
32572             if(this.disabled){
32573                 return;
32574             }
32575
32576             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32577                 this.node.toggle();
32578             }
32579
32580             this.fireEvent("click", this.node, e);
32581         }else{
32582             e.stopEvent();
32583         }
32584     },
32585
32586     onDblClick : function(e){
32587         e.preventDefault();
32588         if(this.disabled){
32589             return;
32590         }
32591         if(this.checkbox){
32592             this.toggleCheck();
32593         }
32594         if(!this.animating && this.node.hasChildNodes()){
32595             this.node.toggle();
32596         }
32597         this.fireEvent("dblclick", this.node, e);
32598     },
32599
32600     onCheckChange : function(){
32601         var checked = this.checkbox.checked;
32602         this.node.attributes.checked = checked;
32603         this.fireEvent('checkchange', this.node, checked);
32604     },
32605
32606     ecClick : function(e){
32607         if(!this.animating && this.node.hasChildNodes()){
32608             this.node.toggle();
32609         }
32610     },
32611
32612     startDrop : function(){
32613         this.dropping = true;
32614     },
32615
32616     // delayed drop so the click event doesn't get fired on a drop
32617     endDrop : function(){
32618        setTimeout(function(){
32619            this.dropping = false;
32620        }.createDelegate(this), 50);
32621     },
32622
32623     expand : function(){
32624         this.updateExpandIcon();
32625         this.ctNode.style.display = "";
32626     },
32627
32628     focus : function(){
32629         if(!this.node.preventHScroll){
32630             try{this.anchor.focus();
32631             }catch(e){}
32632         }else if(!Roo.isIE){
32633             try{
32634                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32635                 var l = noscroll.scrollLeft;
32636                 this.anchor.focus();
32637                 noscroll.scrollLeft = l;
32638             }catch(e){}
32639         }
32640     },
32641
32642     toggleCheck : function(value){
32643         var cb = this.checkbox;
32644         if(cb){
32645             cb.checked = (value === undefined ? !cb.checked : value);
32646         }
32647     },
32648
32649     blur : function(){
32650         try{
32651             this.anchor.blur();
32652         }catch(e){}
32653     },
32654
32655     animExpand : function(callback){
32656         var ct = Roo.get(this.ctNode);
32657         ct.stopFx();
32658         if(!this.node.hasChildNodes()){
32659             this.updateExpandIcon();
32660             this.ctNode.style.display = "";
32661             Roo.callback(callback);
32662             return;
32663         }
32664         this.animating = true;
32665         this.updateExpandIcon();
32666
32667         ct.slideIn('t', {
32668            callback : function(){
32669                this.animating = false;
32670                Roo.callback(callback);
32671             },
32672             scope: this,
32673             duration: this.node.ownerTree.duration || .25
32674         });
32675     },
32676
32677     highlight : function(){
32678         var tree = this.node.getOwnerTree();
32679         Roo.fly(this.wrap).highlight(
32680             tree.hlColor || "C3DAF9",
32681             {endColor: tree.hlBaseColor}
32682         );
32683     },
32684
32685     collapse : function(){
32686         this.updateExpandIcon();
32687         this.ctNode.style.display = "none";
32688     },
32689
32690     animCollapse : function(callback){
32691         var ct = Roo.get(this.ctNode);
32692         ct.enableDisplayMode('block');
32693         ct.stopFx();
32694
32695         this.animating = true;
32696         this.updateExpandIcon();
32697
32698         ct.slideOut('t', {
32699             callback : function(){
32700                this.animating = false;
32701                Roo.callback(callback);
32702             },
32703             scope: this,
32704             duration: this.node.ownerTree.duration || .25
32705         });
32706     },
32707
32708     getContainer : function(){
32709         return this.ctNode;
32710     },
32711
32712     getEl : function(){
32713         return this.wrap;
32714     },
32715
32716     appendDDGhost : function(ghostNode){
32717         ghostNode.appendChild(this.elNode.cloneNode(true));
32718     },
32719
32720     getDDRepairXY : function(){
32721         return Roo.lib.Dom.getXY(this.iconNode);
32722     },
32723
32724     onRender : function(){
32725         this.render();
32726     },
32727
32728     render : function(bulkRender){
32729         var n = this.node, a = n.attributes;
32730         var targetNode = n.parentNode ?
32731               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32732
32733         if(!this.rendered){
32734             this.rendered = true;
32735
32736             this.renderElements(n, a, targetNode, bulkRender);
32737
32738             if(a.qtip){
32739                if(this.textNode.setAttributeNS){
32740                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32741                    if(a.qtipTitle){
32742                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32743                    }
32744                }else{
32745                    this.textNode.setAttribute("ext:qtip", a.qtip);
32746                    if(a.qtipTitle){
32747                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32748                    }
32749                }
32750             }else if(a.qtipCfg){
32751                 a.qtipCfg.target = Roo.id(this.textNode);
32752                 Roo.QuickTips.register(a.qtipCfg);
32753             }
32754             this.initEvents();
32755             if(!this.node.expanded){
32756                 this.updateExpandIcon();
32757             }
32758         }else{
32759             if(bulkRender === true) {
32760                 targetNode.appendChild(this.wrap);
32761             }
32762         }
32763     },
32764
32765     renderElements : function(n, a, targetNode, bulkRender)
32766     {
32767         // add some indent caching, this helps performance when rendering a large tree
32768         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32769         var t = n.getOwnerTree();
32770         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32771         if (typeof(n.attributes.html) != 'undefined') {
32772             txt = n.attributes.html;
32773         }
32774         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32775         var cb = typeof a.checked == 'boolean';
32776         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32777         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32778             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32779             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32780             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32781             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32782             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32783              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32784                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32785             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32786             "</li>"];
32787
32788         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32789             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32790                                 n.nextSibling.ui.getEl(), buf.join(""));
32791         }else{
32792             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32793         }
32794
32795         this.elNode = this.wrap.childNodes[0];
32796         this.ctNode = this.wrap.childNodes[1];
32797         var cs = this.elNode.childNodes;
32798         this.indentNode = cs[0];
32799         this.ecNode = cs[1];
32800         this.iconNode = cs[2];
32801         var index = 3;
32802         if(cb){
32803             this.checkbox = cs[3];
32804             index++;
32805         }
32806         this.anchor = cs[index];
32807         this.textNode = cs[index].firstChild;
32808     },
32809
32810     getAnchor : function(){
32811         return this.anchor;
32812     },
32813
32814     getTextEl : function(){
32815         return this.textNode;
32816     },
32817
32818     getIconEl : function(){
32819         return this.iconNode;
32820     },
32821
32822     isChecked : function(){
32823         return this.checkbox ? this.checkbox.checked : false;
32824     },
32825
32826     updateExpandIcon : function(){
32827         if(this.rendered){
32828             var n = this.node, c1, c2;
32829             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32830             var hasChild = n.hasChildNodes();
32831             if(hasChild){
32832                 if(n.expanded){
32833                     cls += "-minus";
32834                     c1 = "x-tree-node-collapsed";
32835                     c2 = "x-tree-node-expanded";
32836                 }else{
32837                     cls += "-plus";
32838                     c1 = "x-tree-node-expanded";
32839                     c2 = "x-tree-node-collapsed";
32840                 }
32841                 if(this.wasLeaf){
32842                     this.removeClass("x-tree-node-leaf");
32843                     this.wasLeaf = false;
32844                 }
32845                 if(this.c1 != c1 || this.c2 != c2){
32846                     Roo.fly(this.elNode).replaceClass(c1, c2);
32847                     this.c1 = c1; this.c2 = c2;
32848                 }
32849             }else{
32850                 // this changes non-leafs into leafs if they have no children.
32851                 // it's not very rational behaviour..
32852                 
32853                 if(!this.wasLeaf && this.node.leaf){
32854                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32855                     delete this.c1;
32856                     delete this.c2;
32857                     this.wasLeaf = true;
32858                 }
32859             }
32860             var ecc = "x-tree-ec-icon "+cls;
32861             if(this.ecc != ecc){
32862                 this.ecNode.className = ecc;
32863                 this.ecc = ecc;
32864             }
32865         }
32866     },
32867
32868     getChildIndent : function(){
32869         if(!this.childIndent){
32870             var buf = [];
32871             var p = this.node;
32872             while(p){
32873                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32874                     if(!p.isLast()) {
32875                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32876                     } else {
32877                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32878                     }
32879                 }
32880                 p = p.parentNode;
32881             }
32882             this.childIndent = buf.join("");
32883         }
32884         return this.childIndent;
32885     },
32886
32887     renderIndent : function(){
32888         if(this.rendered){
32889             var indent = "";
32890             var p = this.node.parentNode;
32891             if(p){
32892                 indent = p.ui.getChildIndent();
32893             }
32894             if(this.indentMarkup != indent){ // don't rerender if not required
32895                 this.indentNode.innerHTML = indent;
32896                 this.indentMarkup = indent;
32897             }
32898             this.updateExpandIcon();
32899         }
32900     }
32901 };
32902
32903 Roo.tree.RootTreeNodeUI = function(){
32904     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32905 };
32906 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32907     render : function(){
32908         if(!this.rendered){
32909             var targetNode = this.node.ownerTree.innerCt.dom;
32910             this.node.expanded = true;
32911             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32912             this.wrap = this.ctNode = targetNode.firstChild;
32913         }
32914     },
32915     collapse : function(){
32916     },
32917     expand : function(){
32918     }
32919 });/*
32920  * Based on:
32921  * Ext JS Library 1.1.1
32922  * Copyright(c) 2006-2007, Ext JS, LLC.
32923  *
32924  * Originally Released Under LGPL - original licence link has changed is not relivant.
32925  *
32926  * Fork - LGPL
32927  * <script type="text/javascript">
32928  */
32929 /**
32930  * @class Roo.tree.TreeLoader
32931  * @extends Roo.util.Observable
32932  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32933  * nodes from a specified URL. The response must be a javascript Array definition
32934  * who's elements are node definition objects. eg:
32935  * <pre><code>
32936 {  success : true,
32937    data :      [
32938    
32939     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
32940     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
32941     ]
32942 }
32943
32944
32945 </code></pre>
32946  * <br><br>
32947  * The old style respose with just an array is still supported, but not recommended.
32948  * <br><br>
32949  *
32950  * A server request is sent, and child nodes are loaded only when a node is expanded.
32951  * The loading node's id is passed to the server under the parameter name "node" to
32952  * enable the server to produce the correct child nodes.
32953  * <br><br>
32954  * To pass extra parameters, an event handler may be attached to the "beforeload"
32955  * event, and the parameters specified in the TreeLoader's baseParams property:
32956  * <pre><code>
32957     myTreeLoader.on("beforeload", function(treeLoader, node) {
32958         this.baseParams.category = node.attributes.category;
32959     }, this);
32960 </code></pre><
32961  * This would pass an HTTP parameter called "category" to the server containing
32962  * the value of the Node's "category" attribute.
32963  * @constructor
32964  * Creates a new Treeloader.
32965  * @param {Object} config A config object containing config properties.
32966  */
32967 Roo.tree.TreeLoader = function(config){
32968     this.baseParams = {};
32969     this.requestMethod = "POST";
32970     Roo.apply(this, config);
32971
32972     this.addEvents({
32973     
32974         /**
32975          * @event beforeload
32976          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32977          * @param {Object} This TreeLoader object.
32978          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32979          * @param {Object} callback The callback function specified in the {@link #load} call.
32980          */
32981         beforeload : true,
32982         /**
32983          * @event load
32984          * Fires when the node has been successfuly loaded.
32985          * @param {Object} This TreeLoader object.
32986          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32987          * @param {Object} response The response object containing the data from the server.
32988          */
32989         load : true,
32990         /**
32991          * @event loadexception
32992          * Fires if the network request failed.
32993          * @param {Object} This TreeLoader object.
32994          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32995          * @param {Object} response The response object containing the data from the server.
32996          */
32997         loadexception : true,
32998         /**
32999          * @event create
33000          * Fires before a node is created, enabling you to return custom Node types 
33001          * @param {Object} This TreeLoader object.
33002          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33003          */
33004         create : true
33005     });
33006
33007     Roo.tree.TreeLoader.superclass.constructor.call(this);
33008 };
33009
33010 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33011     /**
33012     * @cfg {String} dataUrl The URL from which to request a Json string which
33013     * specifies an array of node definition object representing the child nodes
33014     * to be loaded.
33015     */
33016     /**
33017     * @cfg {Object} baseParams (optional) An object containing properties which
33018     * specify HTTP parameters to be passed to each request for child nodes.
33019     */
33020     /**
33021     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33022     * created by this loader. If the attributes sent by the server have an attribute in this object,
33023     * they take priority.
33024     */
33025     /**
33026     * @cfg {Object} uiProviders (optional) An object containing properties which
33027     * 
33028     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33029     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33030     * <i>uiProvider</i> attribute of a returned child node is a string rather
33031     * than a reference to a TreeNodeUI implementation, this that string value
33032     * is used as a property name in the uiProviders object. You can define the provider named
33033     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33034     */
33035     uiProviders : {},
33036
33037     /**
33038     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33039     * child nodes before loading.
33040     */
33041     clearOnLoad : true,
33042
33043     /**
33044     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33045     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33046     * Grid query { data : [ .....] }
33047     */
33048     
33049     root : false,
33050      /**
33051     * @cfg {String} queryParam (optional) 
33052     * Name of the query as it will be passed on the querystring (defaults to 'node')
33053     * eg. the request will be ?node=[id]
33054     */
33055     
33056     
33057     queryParam: false,
33058     
33059     /**
33060      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33061      * This is called automatically when a node is expanded, but may be used to reload
33062      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33063      * @param {Roo.tree.TreeNode} node
33064      * @param {Function} callback
33065      */
33066     load : function(node, callback){
33067         if(this.clearOnLoad){
33068             while(node.firstChild){
33069                 node.removeChild(node.firstChild);
33070             }
33071         }
33072         if(node.attributes.children){ // preloaded json children
33073             var cs = node.attributes.children;
33074             for(var i = 0, len = cs.length; i < len; i++){
33075                 node.appendChild(this.createNode(cs[i]));
33076             }
33077             if(typeof callback == "function"){
33078                 callback();
33079             }
33080         }else if(this.dataUrl){
33081             this.requestData(node, callback);
33082         }
33083     },
33084
33085     getParams: function(node){
33086         var buf = [], bp = this.baseParams;
33087         for(var key in bp){
33088             if(typeof bp[key] != "function"){
33089                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33090             }
33091         }
33092         var n = this.queryParam === false ? 'node' : this.queryParam;
33093         buf.push(n + "=", encodeURIComponent(node.id));
33094         return buf.join("");
33095     },
33096
33097     requestData : function(node, callback){
33098         if(this.fireEvent("beforeload", this, node, callback) !== false){
33099             this.transId = Roo.Ajax.request({
33100                 method:this.requestMethod,
33101                 url: this.dataUrl||this.url,
33102                 success: this.handleResponse,
33103                 failure: this.handleFailure,
33104                 scope: this,
33105                 argument: {callback: callback, node: node},
33106                 params: this.getParams(node)
33107             });
33108         }else{
33109             // if the load is cancelled, make sure we notify
33110             // the node that we are done
33111             if(typeof callback == "function"){
33112                 callback();
33113             }
33114         }
33115     },
33116
33117     isLoading : function(){
33118         return this.transId ? true : false;
33119     },
33120
33121     abort : function(){
33122         if(this.isLoading()){
33123             Roo.Ajax.abort(this.transId);
33124         }
33125     },
33126
33127     // private
33128     createNode : function(attr)
33129     {
33130         // apply baseAttrs, nice idea Corey!
33131         if(this.baseAttrs){
33132             Roo.applyIf(attr, this.baseAttrs);
33133         }
33134         if(this.applyLoader !== false){
33135             attr.loader = this;
33136         }
33137         // uiProvider = depreciated..
33138         
33139         if(typeof(attr.uiProvider) == 'string'){
33140            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33141                 /**  eval:var:attr */ eval(attr.uiProvider);
33142         }
33143         if(typeof(this.uiProviders['default']) != 'undefined') {
33144             attr.uiProvider = this.uiProviders['default'];
33145         }
33146         
33147         this.fireEvent('create', this, attr);
33148         
33149         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33150         return(attr.leaf ?
33151                         new Roo.tree.TreeNode(attr) :
33152                         new Roo.tree.AsyncTreeNode(attr));
33153     },
33154
33155     processResponse : function(response, node, callback)
33156     {
33157         var json = response.responseText;
33158         try {
33159             
33160             var o = Roo.decode(json);
33161             
33162             if (this.root === false && typeof(o.success) != undefined) {
33163                 this.root = 'data'; // the default behaviour for list like data..
33164                 }
33165                 
33166             if (this.root !== false &&  !o.success) {
33167                 // it's a failure condition.
33168                 var a = response.argument;
33169                 this.fireEvent("loadexception", this, a.node, response);
33170                 Roo.log("Load failed - should have a handler really");
33171                 return;
33172             }
33173             
33174             
33175             
33176             if (this.root !== false) {
33177                  o = o[this.root];
33178             }
33179             
33180             for(var i = 0, len = o.length; i < len; i++){
33181                 var n = this.createNode(o[i]);
33182                 if(n){
33183                     node.appendChild(n);
33184                 }
33185             }
33186             if(typeof callback == "function"){
33187                 callback(this, node);
33188             }
33189         }catch(e){
33190             this.handleFailure(response);
33191         }
33192     },
33193
33194     handleResponse : function(response){
33195         this.transId = false;
33196         var a = response.argument;
33197         this.processResponse(response, a.node, a.callback);
33198         this.fireEvent("load", this, a.node, response);
33199     },
33200
33201     handleFailure : function(response)
33202     {
33203         // should handle failure better..
33204         this.transId = false;
33205         var a = response.argument;
33206         this.fireEvent("loadexception", this, a.node, response);
33207         if(typeof a.callback == "function"){
33208             a.callback(this, a.node);
33209         }
33210     }
33211 });/*
33212  * Based on:
33213  * Ext JS Library 1.1.1
33214  * Copyright(c) 2006-2007, Ext JS, LLC.
33215  *
33216  * Originally Released Under LGPL - original licence link has changed is not relivant.
33217  *
33218  * Fork - LGPL
33219  * <script type="text/javascript">
33220  */
33221
33222 /**
33223 * @class Roo.tree.TreeFilter
33224 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33225 * @param {TreePanel} tree
33226 * @param {Object} config (optional)
33227  */
33228 Roo.tree.TreeFilter = function(tree, config){
33229     this.tree = tree;
33230     this.filtered = {};
33231     Roo.apply(this, config);
33232 };
33233
33234 Roo.tree.TreeFilter.prototype = {
33235     clearBlank:false,
33236     reverse:false,
33237     autoClear:false,
33238     remove:false,
33239
33240      /**
33241      * Filter the data by a specific attribute.
33242      * @param {String/RegExp} value Either string that the attribute value
33243      * should start with or a RegExp to test against the attribute
33244      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33245      * @param {TreeNode} startNode (optional) The node to start the filter at.
33246      */
33247     filter : function(value, attr, startNode){
33248         attr = attr || "text";
33249         var f;
33250         if(typeof value == "string"){
33251             var vlen = value.length;
33252             // auto clear empty filter
33253             if(vlen == 0 && this.clearBlank){
33254                 this.clear();
33255                 return;
33256             }
33257             value = value.toLowerCase();
33258             f = function(n){
33259                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33260             };
33261         }else if(value.exec){ // regex?
33262             f = function(n){
33263                 return value.test(n.attributes[attr]);
33264             };
33265         }else{
33266             throw 'Illegal filter type, must be string or regex';
33267         }
33268         this.filterBy(f, null, startNode);
33269         },
33270
33271     /**
33272      * Filter by a function. The passed function will be called with each
33273      * node in the tree (or from the startNode). If the function returns true, the node is kept
33274      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33275      * @param {Function} fn The filter function
33276      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33277      */
33278     filterBy : function(fn, scope, startNode){
33279         startNode = startNode || this.tree.root;
33280         if(this.autoClear){
33281             this.clear();
33282         }
33283         var af = this.filtered, rv = this.reverse;
33284         var f = function(n){
33285             if(n == startNode){
33286                 return true;
33287             }
33288             if(af[n.id]){
33289                 return false;
33290             }
33291             var m = fn.call(scope || n, n);
33292             if(!m || rv){
33293                 af[n.id] = n;
33294                 n.ui.hide();
33295                 return false;
33296             }
33297             return true;
33298         };
33299         startNode.cascade(f);
33300         if(this.remove){
33301            for(var id in af){
33302                if(typeof id != "function"){
33303                    var n = af[id];
33304                    if(n && n.parentNode){
33305                        n.parentNode.removeChild(n);
33306                    }
33307                }
33308            }
33309         }
33310     },
33311
33312     /**
33313      * Clears the current filter. Note: with the "remove" option
33314      * set a filter cannot be cleared.
33315      */
33316     clear : function(){
33317         var t = this.tree;
33318         var af = this.filtered;
33319         for(var id in af){
33320             if(typeof id != "function"){
33321                 var n = af[id];
33322                 if(n){
33323                     n.ui.show();
33324                 }
33325             }
33326         }
33327         this.filtered = {};
33328     }
33329 };
33330 /*
33331  * Based on:
33332  * Ext JS Library 1.1.1
33333  * Copyright(c) 2006-2007, Ext JS, LLC.
33334  *
33335  * Originally Released Under LGPL - original licence link has changed is not relivant.
33336  *
33337  * Fork - LGPL
33338  * <script type="text/javascript">
33339  */
33340  
33341
33342 /**
33343  * @class Roo.tree.TreeSorter
33344  * Provides sorting of nodes in a TreePanel
33345  * 
33346  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33347  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33348  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33349  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33350  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33351  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33352  * @constructor
33353  * @param {TreePanel} tree
33354  * @param {Object} config
33355  */
33356 Roo.tree.TreeSorter = function(tree, config){
33357     Roo.apply(this, config);
33358     tree.on("beforechildrenrendered", this.doSort, this);
33359     tree.on("append", this.updateSort, this);
33360     tree.on("insert", this.updateSort, this);
33361     
33362     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33363     var p = this.property || "text";
33364     var sortType = this.sortType;
33365     var fs = this.folderSort;
33366     var cs = this.caseSensitive === true;
33367     var leafAttr = this.leafAttr || 'leaf';
33368
33369     this.sortFn = function(n1, n2){
33370         if(fs){
33371             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33372                 return 1;
33373             }
33374             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33375                 return -1;
33376             }
33377         }
33378         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33379         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33380         if(v1 < v2){
33381                         return dsc ? +1 : -1;
33382                 }else if(v1 > v2){
33383                         return dsc ? -1 : +1;
33384         }else{
33385                 return 0;
33386         }
33387     };
33388 };
33389
33390 Roo.tree.TreeSorter.prototype = {
33391     doSort : function(node){
33392         node.sort(this.sortFn);
33393     },
33394     
33395     compareNodes : function(n1, n2){
33396         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33397     },
33398     
33399     updateSort : function(tree, node){
33400         if(node.childrenRendered){
33401             this.doSort.defer(1, this, [node]);
33402         }
33403     }
33404 };/*
33405  * Based on:
33406  * Ext JS Library 1.1.1
33407  * Copyright(c) 2006-2007, Ext JS, LLC.
33408  *
33409  * Originally Released Under LGPL - original licence link has changed is not relivant.
33410  *
33411  * Fork - LGPL
33412  * <script type="text/javascript">
33413  */
33414
33415 if(Roo.dd.DropZone){
33416     
33417 Roo.tree.TreeDropZone = function(tree, config){
33418     this.allowParentInsert = false;
33419     this.allowContainerDrop = false;
33420     this.appendOnly = false;
33421     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33422     this.tree = tree;
33423     this.lastInsertClass = "x-tree-no-status";
33424     this.dragOverData = {};
33425 };
33426
33427 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33428     ddGroup : "TreeDD",
33429     
33430     expandDelay : 1000,
33431     
33432     expandNode : function(node){
33433         if(node.hasChildNodes() && !node.isExpanded()){
33434             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33435         }
33436     },
33437     
33438     queueExpand : function(node){
33439         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33440     },
33441     
33442     cancelExpand : function(){
33443         if(this.expandProcId){
33444             clearTimeout(this.expandProcId);
33445             this.expandProcId = false;
33446         }
33447     },
33448     
33449     isValidDropPoint : function(n, pt, dd, e, data){
33450         if(!n || !data){ return false; }
33451         var targetNode = n.node;
33452         var dropNode = data.node;
33453         // default drop rules
33454         if(!(targetNode && targetNode.isTarget && pt)){
33455             return false;
33456         }
33457         if(pt == "append" && targetNode.allowChildren === false){
33458             return false;
33459         }
33460         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33461             return false;
33462         }
33463         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33464             return false;
33465         }
33466         // reuse the object
33467         var overEvent = this.dragOverData;
33468         overEvent.tree = this.tree;
33469         overEvent.target = targetNode;
33470         overEvent.data = data;
33471         overEvent.point = pt;
33472         overEvent.source = dd;
33473         overEvent.rawEvent = e;
33474         overEvent.dropNode = dropNode;
33475         overEvent.cancel = false;  
33476         var result = this.tree.fireEvent("nodedragover", overEvent);
33477         return overEvent.cancel === false && result !== false;
33478     },
33479     
33480     getDropPoint : function(e, n, dd){
33481         var tn = n.node;
33482         if(tn.isRoot){
33483             return tn.allowChildren !== false ? "append" : false; // always append for root
33484         }
33485         var dragEl = n.ddel;
33486         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33487         var y = Roo.lib.Event.getPageY(e);
33488         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33489         
33490         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33491         var noAppend = tn.allowChildren === false;
33492         if(this.appendOnly || tn.parentNode.allowChildren === false){
33493             return noAppend ? false : "append";
33494         }
33495         var noBelow = false;
33496         if(!this.allowParentInsert){
33497             noBelow = tn.hasChildNodes() && tn.isExpanded();
33498         }
33499         var q = (b - t) / (noAppend ? 2 : 3);
33500         if(y >= t && y < (t + q)){
33501             return "above";
33502         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33503             return "below";
33504         }else{
33505             return "append";
33506         }
33507     },
33508     
33509     onNodeEnter : function(n, dd, e, data){
33510         this.cancelExpand();
33511     },
33512     
33513     onNodeOver : function(n, dd, e, data){
33514         var pt = this.getDropPoint(e, n, dd);
33515         var node = n.node;
33516         
33517         // auto node expand check
33518         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33519             this.queueExpand(node);
33520         }else if(pt != "append"){
33521             this.cancelExpand();
33522         }
33523         
33524         // set the insert point style on the target node
33525         var returnCls = this.dropNotAllowed;
33526         if(this.isValidDropPoint(n, pt, dd, e, data)){
33527            if(pt){
33528                var el = n.ddel;
33529                var cls;
33530                if(pt == "above"){
33531                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33532                    cls = "x-tree-drag-insert-above";
33533                }else if(pt == "below"){
33534                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33535                    cls = "x-tree-drag-insert-below";
33536                }else{
33537                    returnCls = "x-tree-drop-ok-append";
33538                    cls = "x-tree-drag-append";
33539                }
33540                if(this.lastInsertClass != cls){
33541                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33542                    this.lastInsertClass = cls;
33543                }
33544            }
33545        }
33546        return returnCls;
33547     },
33548     
33549     onNodeOut : function(n, dd, e, data){
33550         this.cancelExpand();
33551         this.removeDropIndicators(n);
33552     },
33553     
33554     onNodeDrop : function(n, dd, e, data){
33555         var point = this.getDropPoint(e, n, dd);
33556         var targetNode = n.node;
33557         targetNode.ui.startDrop();
33558         if(!this.isValidDropPoint(n, point, dd, e, data)){
33559             targetNode.ui.endDrop();
33560             return false;
33561         }
33562         // first try to find the drop node
33563         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33564         var dropEvent = {
33565             tree : this.tree,
33566             target: targetNode,
33567             data: data,
33568             point: point,
33569             source: dd,
33570             rawEvent: e,
33571             dropNode: dropNode,
33572             cancel: !dropNode   
33573         };
33574         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33575         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33576             targetNode.ui.endDrop();
33577             return false;
33578         }
33579         // allow target changing
33580         targetNode = dropEvent.target;
33581         if(point == "append" && !targetNode.isExpanded()){
33582             targetNode.expand(false, null, function(){
33583                 this.completeDrop(dropEvent);
33584             }.createDelegate(this));
33585         }else{
33586             this.completeDrop(dropEvent);
33587         }
33588         return true;
33589     },
33590     
33591     completeDrop : function(de){
33592         var ns = de.dropNode, p = de.point, t = de.target;
33593         if(!(ns instanceof Array)){
33594             ns = [ns];
33595         }
33596         var n;
33597         for(var i = 0, len = ns.length; i < len; i++){
33598             n = ns[i];
33599             if(p == "above"){
33600                 t.parentNode.insertBefore(n, t);
33601             }else if(p == "below"){
33602                 t.parentNode.insertBefore(n, t.nextSibling);
33603             }else{
33604                 t.appendChild(n);
33605             }
33606         }
33607         n.ui.focus();
33608         if(this.tree.hlDrop){
33609             n.ui.highlight();
33610         }
33611         t.ui.endDrop();
33612         this.tree.fireEvent("nodedrop", de);
33613     },
33614     
33615     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33616         if(this.tree.hlDrop){
33617             dropNode.ui.focus();
33618             dropNode.ui.highlight();
33619         }
33620         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33621     },
33622     
33623     getTree : function(){
33624         return this.tree;
33625     },
33626     
33627     removeDropIndicators : function(n){
33628         if(n && n.ddel){
33629             var el = n.ddel;
33630             Roo.fly(el).removeClass([
33631                     "x-tree-drag-insert-above",
33632                     "x-tree-drag-insert-below",
33633                     "x-tree-drag-append"]);
33634             this.lastInsertClass = "_noclass";
33635         }
33636     },
33637     
33638     beforeDragDrop : function(target, e, id){
33639         this.cancelExpand();
33640         return true;
33641     },
33642     
33643     afterRepair : function(data){
33644         if(data && Roo.enableFx){
33645             data.node.ui.highlight();
33646         }
33647         this.hideProxy();
33648     }    
33649 });
33650
33651 }
33652 /*
33653  * Based on:
33654  * Ext JS Library 1.1.1
33655  * Copyright(c) 2006-2007, Ext JS, LLC.
33656  *
33657  * Originally Released Under LGPL - original licence link has changed is not relivant.
33658  *
33659  * Fork - LGPL
33660  * <script type="text/javascript">
33661  */
33662  
33663
33664 if(Roo.dd.DragZone){
33665 Roo.tree.TreeDragZone = function(tree, config){
33666     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33667     this.tree = tree;
33668 };
33669
33670 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33671     ddGroup : "TreeDD",
33672     
33673     onBeforeDrag : function(data, e){
33674         var n = data.node;
33675         return n && n.draggable && !n.disabled;
33676     },
33677     
33678     onInitDrag : function(e){
33679         var data = this.dragData;
33680         this.tree.getSelectionModel().select(data.node);
33681         this.proxy.update("");
33682         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33683         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33684     },
33685     
33686     getRepairXY : function(e, data){
33687         return data.node.ui.getDDRepairXY();
33688     },
33689     
33690     onEndDrag : function(data, e){
33691         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33692     },
33693     
33694     onValidDrop : function(dd, e, id){
33695         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33696         this.hideProxy();
33697     },
33698     
33699     beforeInvalidDrop : function(e, id){
33700         // this scrolls the original position back into view
33701         var sm = this.tree.getSelectionModel();
33702         sm.clearSelections();
33703         sm.select(this.dragData.node);
33704     }
33705 });
33706 }/*
33707  * Based on:
33708  * Ext JS Library 1.1.1
33709  * Copyright(c) 2006-2007, Ext JS, LLC.
33710  *
33711  * Originally Released Under LGPL - original licence link has changed is not relivant.
33712  *
33713  * Fork - LGPL
33714  * <script type="text/javascript">
33715  */
33716 /**
33717  * @class Roo.tree.TreeEditor
33718  * @extends Roo.Editor
33719  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33720  * as the editor field.
33721  * @constructor
33722  * @param {Object} config (used to be the tree panel.)
33723  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33724  * 
33725  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33726  * @cfg {Roo.form.TextField|Object} field The field configuration
33727  *
33728  * 
33729  */
33730 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33731     var tree = config;
33732     var field;
33733     if (oldconfig) { // old style..
33734         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33735     } else {
33736         // new style..
33737         tree = config.tree;
33738         config.field = config.field  || {};
33739         config.field.xtype = 'TextField';
33740         field = Roo.factory(config.field, Roo.form);
33741     }
33742     config = config || {};
33743     
33744     
33745     this.addEvents({
33746         /**
33747          * @event beforenodeedit
33748          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33749          * false from the handler of this event.
33750          * @param {Editor} this
33751          * @param {Roo.tree.Node} node 
33752          */
33753         "beforenodeedit" : true
33754     });
33755     
33756     //Roo.log(config);
33757     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33758
33759     this.tree = tree;
33760
33761     tree.on('beforeclick', this.beforeNodeClick, this);
33762     tree.getTreeEl().on('mousedown', this.hide, this);
33763     this.on('complete', this.updateNode, this);
33764     this.on('beforestartedit', this.fitToTree, this);
33765     this.on('startedit', this.bindScroll, this, {delay:10});
33766     this.on('specialkey', this.onSpecialKey, this);
33767 };
33768
33769 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33770     /**
33771      * @cfg {String} alignment
33772      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33773      */
33774     alignment: "l-l",
33775     // inherit
33776     autoSize: false,
33777     /**
33778      * @cfg {Boolean} hideEl
33779      * True to hide the bound element while the editor is displayed (defaults to false)
33780      */
33781     hideEl : false,
33782     /**
33783      * @cfg {String} cls
33784      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33785      */
33786     cls: "x-small-editor x-tree-editor",
33787     /**
33788      * @cfg {Boolean} shim
33789      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33790      */
33791     shim:false,
33792     // inherit
33793     shadow:"frame",
33794     /**
33795      * @cfg {Number} maxWidth
33796      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33797      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33798      * scroll and client offsets into account prior to each edit.
33799      */
33800     maxWidth: 250,
33801
33802     editDelay : 350,
33803
33804     // private
33805     fitToTree : function(ed, el){
33806         var td = this.tree.getTreeEl().dom, nd = el.dom;
33807         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33808             td.scrollLeft = nd.offsetLeft;
33809         }
33810         var w = Math.min(
33811                 this.maxWidth,
33812                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33813         this.setSize(w, '');
33814         
33815         return this.fireEvent('beforenodeedit', this, this.editNode);
33816         
33817     },
33818
33819     // private
33820     triggerEdit : function(node){
33821         this.completeEdit();
33822         this.editNode = node;
33823         this.startEdit(node.ui.textNode, node.text);
33824     },
33825
33826     // private
33827     bindScroll : function(){
33828         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33829     },
33830
33831     // private
33832     beforeNodeClick : function(node, e){
33833         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33834         this.lastClick = new Date();
33835         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33836             e.stopEvent();
33837             this.triggerEdit(node);
33838             return false;
33839         }
33840         return true;
33841     },
33842
33843     // private
33844     updateNode : function(ed, value){
33845         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33846         this.editNode.setText(value);
33847     },
33848
33849     // private
33850     onHide : function(){
33851         Roo.tree.TreeEditor.superclass.onHide.call(this);
33852         if(this.editNode){
33853             this.editNode.ui.focus();
33854         }
33855     },
33856
33857     // private
33858     onSpecialKey : function(field, e){
33859         var k = e.getKey();
33860         if(k == e.ESC){
33861             e.stopEvent();
33862             this.cancelEdit();
33863         }else if(k == e.ENTER && !e.hasModifier()){
33864             e.stopEvent();
33865             this.completeEdit();
33866         }
33867     }
33868 });//<Script type="text/javascript">
33869 /*
33870  * Based on:
33871  * Ext JS Library 1.1.1
33872  * Copyright(c) 2006-2007, Ext JS, LLC.
33873  *
33874  * Originally Released Under LGPL - original licence link has changed is not relivant.
33875  *
33876  * Fork - LGPL
33877  * <script type="text/javascript">
33878  */
33879  
33880 /**
33881  * Not documented??? - probably should be...
33882  */
33883
33884 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33885     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33886     
33887     renderElements : function(n, a, targetNode, bulkRender){
33888         //consel.log("renderElements?");
33889         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33890
33891         var t = n.getOwnerTree();
33892         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33893         
33894         var cols = t.columns;
33895         var bw = t.borderWidth;
33896         var c = cols[0];
33897         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33898          var cb = typeof a.checked == "boolean";
33899         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33900         var colcls = 'x-t-' + tid + '-c0';
33901         var buf = [
33902             '<li class="x-tree-node">',
33903             
33904                 
33905                 '<div class="x-tree-node-el ', a.cls,'">',
33906                     // extran...
33907                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33908                 
33909                 
33910                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33911                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33912                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33913                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33914                            (a.iconCls ? ' '+a.iconCls : ''),
33915                            '" unselectable="on" />',
33916                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33917                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33918                              
33919                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33920                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33921                             '<span unselectable="on" qtip="' + tx + '">',
33922                              tx,
33923                              '</span></a>' ,
33924                     '</div>',
33925                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33926                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33927                  ];
33928         for(var i = 1, len = cols.length; i < len; i++){
33929             c = cols[i];
33930             colcls = 'x-t-' + tid + '-c' +i;
33931             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33932             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33933                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33934                       "</div>");
33935          }
33936          
33937          buf.push(
33938             '</a>',
33939             '<div class="x-clear"></div></div>',
33940             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33941             "</li>");
33942         
33943         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33944             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33945                                 n.nextSibling.ui.getEl(), buf.join(""));
33946         }else{
33947             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33948         }
33949         var el = this.wrap.firstChild;
33950         this.elRow = el;
33951         this.elNode = el.firstChild;
33952         this.ranchor = el.childNodes[1];
33953         this.ctNode = this.wrap.childNodes[1];
33954         var cs = el.firstChild.childNodes;
33955         this.indentNode = cs[0];
33956         this.ecNode = cs[1];
33957         this.iconNode = cs[2];
33958         var index = 3;
33959         if(cb){
33960             this.checkbox = cs[3];
33961             index++;
33962         }
33963         this.anchor = cs[index];
33964         
33965         this.textNode = cs[index].firstChild;
33966         
33967         //el.on("click", this.onClick, this);
33968         //el.on("dblclick", this.onDblClick, this);
33969         
33970         
33971        // console.log(this);
33972     },
33973     initEvents : function(){
33974         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33975         
33976             
33977         var a = this.ranchor;
33978
33979         var el = Roo.get(a);
33980
33981         if(Roo.isOpera){ // opera render bug ignores the CSS
33982             el.setStyle("text-decoration", "none");
33983         }
33984
33985         el.on("click", this.onClick, this);
33986         el.on("dblclick", this.onDblClick, this);
33987         el.on("contextmenu", this.onContextMenu, this);
33988         
33989     },
33990     
33991     /*onSelectedChange : function(state){
33992         if(state){
33993             this.focus();
33994             this.addClass("x-tree-selected");
33995         }else{
33996             //this.blur();
33997             this.removeClass("x-tree-selected");
33998         }
33999     },*/
34000     addClass : function(cls){
34001         if(this.elRow){
34002             Roo.fly(this.elRow).addClass(cls);
34003         }
34004         
34005     },
34006     
34007     
34008     removeClass : function(cls){
34009         if(this.elRow){
34010             Roo.fly(this.elRow).removeClass(cls);
34011         }
34012     }
34013
34014     
34015     
34016 });//<Script type="text/javascript">
34017
34018 /*
34019  * Based on:
34020  * Ext JS Library 1.1.1
34021  * Copyright(c) 2006-2007, Ext JS, LLC.
34022  *
34023  * Originally Released Under LGPL - original licence link has changed is not relivant.
34024  *
34025  * Fork - LGPL
34026  * <script type="text/javascript">
34027  */
34028  
34029
34030 /**
34031  * @class Roo.tree.ColumnTree
34032  * @extends Roo.data.TreePanel
34033  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34034  * @cfg {int} borderWidth  compined right/left border allowance
34035  * @constructor
34036  * @param {String/HTMLElement/Element} el The container element
34037  * @param {Object} config
34038  */
34039 Roo.tree.ColumnTree =  function(el, config)
34040 {
34041    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34042    this.addEvents({
34043         /**
34044         * @event resize
34045         * Fire this event on a container when it resizes
34046         * @param {int} w Width
34047         * @param {int} h Height
34048         */
34049        "resize" : true
34050     });
34051     this.on('resize', this.onResize, this);
34052 };
34053
34054 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34055     //lines:false,
34056     
34057     
34058     borderWidth: Roo.isBorderBox ? 0 : 2, 
34059     headEls : false,
34060     
34061     render : function(){
34062         // add the header.....
34063        
34064         Roo.tree.ColumnTree.superclass.render.apply(this);
34065         
34066         this.el.addClass('x-column-tree');
34067         
34068         this.headers = this.el.createChild(
34069             {cls:'x-tree-headers'},this.innerCt.dom);
34070    
34071         var cols = this.columns, c;
34072         var totalWidth = 0;
34073         this.headEls = [];
34074         var  len = cols.length;
34075         for(var i = 0; i < len; i++){
34076              c = cols[i];
34077              totalWidth += c.width;
34078             this.headEls.push(this.headers.createChild({
34079                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34080                  cn: {
34081                      cls:'x-tree-hd-text',
34082                      html: c.header
34083                  },
34084                  style:'width:'+(c.width-this.borderWidth)+'px;'
34085              }));
34086         }
34087         this.headers.createChild({cls:'x-clear'});
34088         // prevent floats from wrapping when clipped
34089         this.headers.setWidth(totalWidth);
34090         //this.innerCt.setWidth(totalWidth);
34091         this.innerCt.setStyle({ overflow: 'auto' });
34092         this.onResize(this.width, this.height);
34093              
34094         
34095     },
34096     onResize : function(w,h)
34097     {
34098         this.height = h;
34099         this.width = w;
34100         // resize cols..
34101         this.innerCt.setWidth(this.width);
34102         this.innerCt.setHeight(this.height-20);
34103         
34104         // headers...
34105         var cols = this.columns, c;
34106         var totalWidth = 0;
34107         var expEl = false;
34108         var len = cols.length;
34109         for(var i = 0; i < len; i++){
34110             c = cols[i];
34111             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34112                 // it's the expander..
34113                 expEl  = this.headEls[i];
34114                 continue;
34115             }
34116             totalWidth += c.width;
34117             
34118         }
34119         if (expEl) {
34120             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34121         }
34122         this.headers.setWidth(w-20);
34123
34124         
34125         
34126         
34127     }
34128 });
34129 /*
34130  * Based on:
34131  * Ext JS Library 1.1.1
34132  * Copyright(c) 2006-2007, Ext JS, LLC.
34133  *
34134  * Originally Released Under LGPL - original licence link has changed is not relivant.
34135  *
34136  * Fork - LGPL
34137  * <script type="text/javascript">
34138  */
34139  
34140 /**
34141  * @class Roo.menu.Menu
34142  * @extends Roo.util.Observable
34143  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34144  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34145  * @constructor
34146  * Creates a new Menu
34147  * @param {Object} config Configuration options
34148  */
34149 Roo.menu.Menu = function(config){
34150     Roo.apply(this, config);
34151     this.id = this.id || Roo.id();
34152     this.addEvents({
34153         /**
34154          * @event beforeshow
34155          * Fires before this menu is displayed
34156          * @param {Roo.menu.Menu} this
34157          */
34158         beforeshow : true,
34159         /**
34160          * @event beforehide
34161          * Fires before this menu is hidden
34162          * @param {Roo.menu.Menu} this
34163          */
34164         beforehide : true,
34165         /**
34166          * @event show
34167          * Fires after this menu is displayed
34168          * @param {Roo.menu.Menu} this
34169          */
34170         show : true,
34171         /**
34172          * @event hide
34173          * Fires after this menu is hidden
34174          * @param {Roo.menu.Menu} this
34175          */
34176         hide : true,
34177         /**
34178          * @event click
34179          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34180          * @param {Roo.menu.Menu} this
34181          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34182          * @param {Roo.EventObject} e
34183          */
34184         click : true,
34185         /**
34186          * @event mouseover
34187          * Fires when the mouse is hovering over this menu
34188          * @param {Roo.menu.Menu} this
34189          * @param {Roo.EventObject} e
34190          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34191          */
34192         mouseover : true,
34193         /**
34194          * @event mouseout
34195          * Fires when the mouse exits this menu
34196          * @param {Roo.menu.Menu} this
34197          * @param {Roo.EventObject} e
34198          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34199          */
34200         mouseout : true,
34201         /**
34202          * @event itemclick
34203          * Fires when a menu item contained in this menu is clicked
34204          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34205          * @param {Roo.EventObject} e
34206          */
34207         itemclick: true
34208     });
34209     if (this.registerMenu) {
34210         Roo.menu.MenuMgr.register(this);
34211     }
34212     
34213     var mis = this.items;
34214     this.items = new Roo.util.MixedCollection();
34215     if(mis){
34216         this.add.apply(this, mis);
34217     }
34218 };
34219
34220 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34221     /**
34222      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34223      */
34224     minWidth : 120,
34225     /**
34226      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34227      * for bottom-right shadow (defaults to "sides")
34228      */
34229     shadow : "sides",
34230     /**
34231      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34232      * this menu (defaults to "tl-tr?")
34233      */
34234     subMenuAlign : "tl-tr?",
34235     /**
34236      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34237      * relative to its element of origin (defaults to "tl-bl?")
34238      */
34239     defaultAlign : "tl-bl?",
34240     /**
34241      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34242      */
34243     allowOtherMenus : false,
34244     /**
34245      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34246      */
34247     registerMenu : true,
34248
34249     hidden:true,
34250
34251     // private
34252     render : function(){
34253         if(this.el){
34254             return;
34255         }
34256         var el = this.el = new Roo.Layer({
34257             cls: "x-menu",
34258             shadow:this.shadow,
34259             constrain: false,
34260             parentEl: this.parentEl || document.body,
34261             zindex:15000
34262         });
34263
34264         this.keyNav = new Roo.menu.MenuNav(this);
34265
34266         if(this.plain){
34267             el.addClass("x-menu-plain");
34268         }
34269         if(this.cls){
34270             el.addClass(this.cls);
34271         }
34272         // generic focus element
34273         this.focusEl = el.createChild({
34274             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34275         });
34276         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34277         ul.on("click", this.onClick, this);
34278         ul.on("mouseover", this.onMouseOver, this);
34279         ul.on("mouseout", this.onMouseOut, this);
34280         this.items.each(function(item){
34281             var li = document.createElement("li");
34282             li.className = "x-menu-list-item";
34283             ul.dom.appendChild(li);
34284             item.render(li, this);
34285         }, this);
34286         this.ul = ul;
34287         this.autoWidth();
34288     },
34289
34290     // private
34291     autoWidth : function(){
34292         var el = this.el, ul = this.ul;
34293         if(!el){
34294             return;
34295         }
34296         var w = this.width;
34297         if(w){
34298             el.setWidth(w);
34299         }else if(Roo.isIE){
34300             el.setWidth(this.minWidth);
34301             var t = el.dom.offsetWidth; // force recalc
34302             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34303         }
34304     },
34305
34306     // private
34307     delayAutoWidth : function(){
34308         if(this.rendered){
34309             if(!this.awTask){
34310                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34311             }
34312             this.awTask.delay(20);
34313         }
34314     },
34315
34316     // private
34317     findTargetItem : function(e){
34318         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34319         if(t && t.menuItemId){
34320             return this.items.get(t.menuItemId);
34321         }
34322     },
34323
34324     // private
34325     onClick : function(e){
34326         var t;
34327         if(t = this.findTargetItem(e)){
34328             t.onClick(e);
34329             this.fireEvent("click", this, t, e);
34330         }
34331     },
34332
34333     // private
34334     setActiveItem : function(item, autoExpand){
34335         if(item != this.activeItem){
34336             if(this.activeItem){
34337                 this.activeItem.deactivate();
34338             }
34339             this.activeItem = item;
34340             item.activate(autoExpand);
34341         }else if(autoExpand){
34342             item.expandMenu();
34343         }
34344     },
34345
34346     // private
34347     tryActivate : function(start, step){
34348         var items = this.items;
34349         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34350             var item = items.get(i);
34351             if(!item.disabled && item.canActivate){
34352                 this.setActiveItem(item, false);
34353                 return item;
34354             }
34355         }
34356         return false;
34357     },
34358
34359     // private
34360     onMouseOver : function(e){
34361         var t;
34362         if(t = this.findTargetItem(e)){
34363             if(t.canActivate && !t.disabled){
34364                 this.setActiveItem(t, true);
34365             }
34366         }
34367         this.fireEvent("mouseover", this, e, t);
34368     },
34369
34370     // private
34371     onMouseOut : function(e){
34372         var t;
34373         if(t = this.findTargetItem(e)){
34374             if(t == this.activeItem && t.shouldDeactivate(e)){
34375                 this.activeItem.deactivate();
34376                 delete this.activeItem;
34377             }
34378         }
34379         this.fireEvent("mouseout", this, e, t);
34380     },
34381
34382     /**
34383      * Read-only.  Returns true if the menu is currently displayed, else false.
34384      * @type Boolean
34385      */
34386     isVisible : function(){
34387         return this.el && !this.hidden;
34388     },
34389
34390     /**
34391      * Displays this menu relative to another element
34392      * @param {String/HTMLElement/Roo.Element} element The element to align to
34393      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34394      * the element (defaults to this.defaultAlign)
34395      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34396      */
34397     show : function(el, pos, parentMenu){
34398         this.parentMenu = parentMenu;
34399         if(!this.el){
34400             this.render();
34401         }
34402         this.fireEvent("beforeshow", this);
34403         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34404     },
34405
34406     /**
34407      * Displays this menu at a specific xy position
34408      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34409      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34410      */
34411     showAt : function(xy, parentMenu, /* private: */_e){
34412         this.parentMenu = parentMenu;
34413         if(!this.el){
34414             this.render();
34415         }
34416         if(_e !== false){
34417             this.fireEvent("beforeshow", this);
34418             xy = this.el.adjustForConstraints(xy);
34419         }
34420         this.el.setXY(xy);
34421         this.el.show();
34422         this.hidden = false;
34423         this.focus();
34424         this.fireEvent("show", this);
34425     },
34426
34427     focus : function(){
34428         if(!this.hidden){
34429             this.doFocus.defer(50, this);
34430         }
34431     },
34432
34433     doFocus : function(){
34434         if(!this.hidden){
34435             this.focusEl.focus();
34436         }
34437     },
34438
34439     /**
34440      * Hides this menu and optionally all parent menus
34441      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34442      */
34443     hide : function(deep){
34444         if(this.el && this.isVisible()){
34445             this.fireEvent("beforehide", this);
34446             if(this.activeItem){
34447                 this.activeItem.deactivate();
34448                 this.activeItem = null;
34449             }
34450             this.el.hide();
34451             this.hidden = true;
34452             this.fireEvent("hide", this);
34453         }
34454         if(deep === true && this.parentMenu){
34455             this.parentMenu.hide(true);
34456         }
34457     },
34458
34459     /**
34460      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34461      * Any of the following are valid:
34462      * <ul>
34463      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34464      * <li>An HTMLElement object which will be converted to a menu item</li>
34465      * <li>A menu item config object that will be created as a new menu item</li>
34466      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34467      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34468      * </ul>
34469      * Usage:
34470      * <pre><code>
34471 // Create the menu
34472 var menu = new Roo.menu.Menu();
34473
34474 // Create a menu item to add by reference
34475 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34476
34477 // Add a bunch of items at once using different methods.
34478 // Only the last item added will be returned.
34479 var item = menu.add(
34480     menuItem,                // add existing item by ref
34481     'Dynamic Item',          // new TextItem
34482     '-',                     // new separator
34483     { text: 'Config Item' }  // new item by config
34484 );
34485 </code></pre>
34486      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34487      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34488      */
34489     add : function(){
34490         var a = arguments, l = a.length, item;
34491         for(var i = 0; i < l; i++){
34492             var el = a[i];
34493             if ((typeof(el) == "object") && el.xtype && el.xns) {
34494                 el = Roo.factory(el, Roo.menu);
34495             }
34496             
34497             if(el.render){ // some kind of Item
34498                 item = this.addItem(el);
34499             }else if(typeof el == "string"){ // string
34500                 if(el == "separator" || el == "-"){
34501                     item = this.addSeparator();
34502                 }else{
34503                     item = this.addText(el);
34504                 }
34505             }else if(el.tagName || el.el){ // element
34506                 item = this.addElement(el);
34507             }else if(typeof el == "object"){ // must be menu item config?
34508                 item = this.addMenuItem(el);
34509             }
34510         }
34511         return item;
34512     },
34513
34514     /**
34515      * Returns this menu's underlying {@link Roo.Element} object
34516      * @return {Roo.Element} The element
34517      */
34518     getEl : function(){
34519         if(!this.el){
34520             this.render();
34521         }
34522         return this.el;
34523     },
34524
34525     /**
34526      * Adds a separator bar to the menu
34527      * @return {Roo.menu.Item} The menu item that was added
34528      */
34529     addSeparator : function(){
34530         return this.addItem(new Roo.menu.Separator());
34531     },
34532
34533     /**
34534      * Adds an {@link Roo.Element} object to the menu
34535      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34536      * @return {Roo.menu.Item} The menu item that was added
34537      */
34538     addElement : function(el){
34539         return this.addItem(new Roo.menu.BaseItem(el));
34540     },
34541
34542     /**
34543      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34544      * @param {Roo.menu.Item} item The menu item to add
34545      * @return {Roo.menu.Item} The menu item that was added
34546      */
34547     addItem : function(item){
34548         this.items.add(item);
34549         if(this.ul){
34550             var li = document.createElement("li");
34551             li.className = "x-menu-list-item";
34552             this.ul.dom.appendChild(li);
34553             item.render(li, this);
34554             this.delayAutoWidth();
34555         }
34556         return item;
34557     },
34558
34559     /**
34560      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34561      * @param {Object} config A MenuItem config object
34562      * @return {Roo.menu.Item} The menu item that was added
34563      */
34564     addMenuItem : function(config){
34565         if(!(config instanceof Roo.menu.Item)){
34566             if(typeof config.checked == "boolean"){ // must be check menu item config?
34567                 config = new Roo.menu.CheckItem(config);
34568             }else{
34569                 config = new Roo.menu.Item(config);
34570             }
34571         }
34572         return this.addItem(config);
34573     },
34574
34575     /**
34576      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34577      * @param {String} text The text to display in the menu item
34578      * @return {Roo.menu.Item} The menu item that was added
34579      */
34580     addText : function(text){
34581         return this.addItem(new Roo.menu.TextItem({ text : text }));
34582     },
34583
34584     /**
34585      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34586      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34587      * @param {Roo.menu.Item} item The menu item to add
34588      * @return {Roo.menu.Item} The menu item that was added
34589      */
34590     insert : function(index, item){
34591         this.items.insert(index, item);
34592         if(this.ul){
34593             var li = document.createElement("li");
34594             li.className = "x-menu-list-item";
34595             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34596             item.render(li, this);
34597             this.delayAutoWidth();
34598         }
34599         return item;
34600     },
34601
34602     /**
34603      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34604      * @param {Roo.menu.Item} item The menu item to remove
34605      */
34606     remove : function(item){
34607         this.items.removeKey(item.id);
34608         item.destroy();
34609     },
34610
34611     /**
34612      * Removes and destroys all items in the menu
34613      */
34614     removeAll : function(){
34615         var f;
34616         while(f = this.items.first()){
34617             this.remove(f);
34618         }
34619     }
34620 });
34621
34622 // MenuNav is a private utility class used internally by the Menu
34623 Roo.menu.MenuNav = function(menu){
34624     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34625     this.scope = this.menu = menu;
34626 };
34627
34628 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34629     doRelay : function(e, h){
34630         var k = e.getKey();
34631         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34632             this.menu.tryActivate(0, 1);
34633             return false;
34634         }
34635         return h.call(this.scope || this, e, this.menu);
34636     },
34637
34638     up : function(e, m){
34639         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34640             m.tryActivate(m.items.length-1, -1);
34641         }
34642     },
34643
34644     down : function(e, m){
34645         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34646             m.tryActivate(0, 1);
34647         }
34648     },
34649
34650     right : function(e, m){
34651         if(m.activeItem){
34652             m.activeItem.expandMenu(true);
34653         }
34654     },
34655
34656     left : function(e, m){
34657         m.hide();
34658         if(m.parentMenu && m.parentMenu.activeItem){
34659             m.parentMenu.activeItem.activate();
34660         }
34661     },
34662
34663     enter : function(e, m){
34664         if(m.activeItem){
34665             e.stopPropagation();
34666             m.activeItem.onClick(e);
34667             m.fireEvent("click", this, m.activeItem);
34668             return true;
34669         }
34670     }
34671 });/*
34672  * Based on:
34673  * Ext JS Library 1.1.1
34674  * Copyright(c) 2006-2007, Ext JS, LLC.
34675  *
34676  * Originally Released Under LGPL - original licence link has changed is not relivant.
34677  *
34678  * Fork - LGPL
34679  * <script type="text/javascript">
34680  */
34681  
34682 /**
34683  * @class Roo.menu.MenuMgr
34684  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34685  * @singleton
34686  */
34687 Roo.menu.MenuMgr = function(){
34688    var menus, active, groups = {}, attached = false, lastShow = new Date();
34689
34690    // private - called when first menu is created
34691    function init(){
34692        menus = {};
34693        active = new Roo.util.MixedCollection();
34694        Roo.get(document).addKeyListener(27, function(){
34695            if(active.length > 0){
34696                hideAll();
34697            }
34698        });
34699    }
34700
34701    // private
34702    function hideAll(){
34703        if(active && active.length > 0){
34704            var c = active.clone();
34705            c.each(function(m){
34706                m.hide();
34707            });
34708        }
34709    }
34710
34711    // private
34712    function onHide(m){
34713        active.remove(m);
34714        if(active.length < 1){
34715            Roo.get(document).un("mousedown", onMouseDown);
34716            attached = false;
34717        }
34718    }
34719
34720    // private
34721    function onShow(m){
34722        var last = active.last();
34723        lastShow = new Date();
34724        active.add(m);
34725        if(!attached){
34726            Roo.get(document).on("mousedown", onMouseDown);
34727            attached = true;
34728        }
34729        if(m.parentMenu){
34730           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34731           m.parentMenu.activeChild = m;
34732        }else if(last && last.isVisible()){
34733           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34734        }
34735    }
34736
34737    // private
34738    function onBeforeHide(m){
34739        if(m.activeChild){
34740            m.activeChild.hide();
34741        }
34742        if(m.autoHideTimer){
34743            clearTimeout(m.autoHideTimer);
34744            delete m.autoHideTimer;
34745        }
34746    }
34747
34748    // private
34749    function onBeforeShow(m){
34750        var pm = m.parentMenu;
34751        if(!pm && !m.allowOtherMenus){
34752            hideAll();
34753        }else if(pm && pm.activeChild && active != m){
34754            pm.activeChild.hide();
34755        }
34756    }
34757
34758    // private
34759    function onMouseDown(e){
34760        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34761            hideAll();
34762        }
34763    }
34764
34765    // private
34766    function onBeforeCheck(mi, state){
34767        if(state){
34768            var g = groups[mi.group];
34769            for(var i = 0, l = g.length; i < l; i++){
34770                if(g[i] != mi){
34771                    g[i].setChecked(false);
34772                }
34773            }
34774        }
34775    }
34776
34777    return {
34778
34779        /**
34780         * Hides all menus that are currently visible
34781         */
34782        hideAll : function(){
34783             hideAll();  
34784        },
34785
34786        // private
34787        register : function(menu){
34788            if(!menus){
34789                init();
34790            }
34791            menus[menu.id] = menu;
34792            menu.on("beforehide", onBeforeHide);
34793            menu.on("hide", onHide);
34794            menu.on("beforeshow", onBeforeShow);
34795            menu.on("show", onShow);
34796            var g = menu.group;
34797            if(g && menu.events["checkchange"]){
34798                if(!groups[g]){
34799                    groups[g] = [];
34800                }
34801                groups[g].push(menu);
34802                menu.on("checkchange", onCheck);
34803            }
34804        },
34805
34806         /**
34807          * Returns a {@link Roo.menu.Menu} object
34808          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34809          * be used to generate and return a new Menu instance.
34810          */
34811        get : function(menu){
34812            if(typeof menu == "string"){ // menu id
34813                return menus[menu];
34814            }else if(menu.events){  // menu instance
34815                return menu;
34816            }else if(typeof menu.length == 'number'){ // array of menu items?
34817                return new Roo.menu.Menu({items:menu});
34818            }else{ // otherwise, must be a config
34819                return new Roo.menu.Menu(menu);
34820            }
34821        },
34822
34823        // private
34824        unregister : function(menu){
34825            delete menus[menu.id];
34826            menu.un("beforehide", onBeforeHide);
34827            menu.un("hide", onHide);
34828            menu.un("beforeshow", onBeforeShow);
34829            menu.un("show", onShow);
34830            var g = menu.group;
34831            if(g && menu.events["checkchange"]){
34832                groups[g].remove(menu);
34833                menu.un("checkchange", onCheck);
34834            }
34835        },
34836
34837        // private
34838        registerCheckable : function(menuItem){
34839            var g = menuItem.group;
34840            if(g){
34841                if(!groups[g]){
34842                    groups[g] = [];
34843                }
34844                groups[g].push(menuItem);
34845                menuItem.on("beforecheckchange", onBeforeCheck);
34846            }
34847        },
34848
34849        // private
34850        unregisterCheckable : function(menuItem){
34851            var g = menuItem.group;
34852            if(g){
34853                groups[g].remove(menuItem);
34854                menuItem.un("beforecheckchange", onBeforeCheck);
34855            }
34856        }
34857    };
34858 }();/*
34859  * Based on:
34860  * Ext JS Library 1.1.1
34861  * Copyright(c) 2006-2007, Ext JS, LLC.
34862  *
34863  * Originally Released Under LGPL - original licence link has changed is not relivant.
34864  *
34865  * Fork - LGPL
34866  * <script type="text/javascript">
34867  */
34868  
34869
34870 /**
34871  * @class Roo.menu.BaseItem
34872  * @extends Roo.Component
34873  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34874  * management and base configuration options shared by all menu components.
34875  * @constructor
34876  * Creates a new BaseItem
34877  * @param {Object} config Configuration options
34878  */
34879 Roo.menu.BaseItem = function(config){
34880     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34881
34882     this.addEvents({
34883         /**
34884          * @event click
34885          * Fires when this item is clicked
34886          * @param {Roo.menu.BaseItem} this
34887          * @param {Roo.EventObject} e
34888          */
34889         click: true,
34890         /**
34891          * @event activate
34892          * Fires when this item is activated
34893          * @param {Roo.menu.BaseItem} this
34894          */
34895         activate : true,
34896         /**
34897          * @event deactivate
34898          * Fires when this item is deactivated
34899          * @param {Roo.menu.BaseItem} this
34900          */
34901         deactivate : true
34902     });
34903
34904     if(this.handler){
34905         this.on("click", this.handler, this.scope, true);
34906     }
34907 };
34908
34909 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34910     /**
34911      * @cfg {Function} handler
34912      * A function that will handle the click event of this menu item (defaults to undefined)
34913      */
34914     /**
34915      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34916      */
34917     canActivate : false,
34918     /**
34919      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34920      */
34921     activeClass : "x-menu-item-active",
34922     /**
34923      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34924      */
34925     hideOnClick : true,
34926     /**
34927      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34928      */
34929     hideDelay : 100,
34930
34931     // private
34932     ctype: "Roo.menu.BaseItem",
34933
34934     // private
34935     actionMode : "container",
34936
34937     // private
34938     render : function(container, parentMenu){
34939         this.parentMenu = parentMenu;
34940         Roo.menu.BaseItem.superclass.render.call(this, container);
34941         this.container.menuItemId = this.id;
34942     },
34943
34944     // private
34945     onRender : function(container, position){
34946         this.el = Roo.get(this.el);
34947         container.dom.appendChild(this.el.dom);
34948     },
34949
34950     // private
34951     onClick : function(e){
34952         if(!this.disabled && this.fireEvent("click", this, e) !== false
34953                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34954             this.handleClick(e);
34955         }else{
34956             e.stopEvent();
34957         }
34958     },
34959
34960     // private
34961     activate : function(){
34962         if(this.disabled){
34963             return false;
34964         }
34965         var li = this.container;
34966         li.addClass(this.activeClass);
34967         this.region = li.getRegion().adjust(2, 2, -2, -2);
34968         this.fireEvent("activate", this);
34969         return true;
34970     },
34971
34972     // private
34973     deactivate : function(){
34974         this.container.removeClass(this.activeClass);
34975         this.fireEvent("deactivate", this);
34976     },
34977
34978     // private
34979     shouldDeactivate : function(e){
34980         return !this.region || !this.region.contains(e.getPoint());
34981     },
34982
34983     // private
34984     handleClick : function(e){
34985         if(this.hideOnClick){
34986             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34987         }
34988     },
34989
34990     // private
34991     expandMenu : function(autoActivate){
34992         // do nothing
34993     },
34994
34995     // private
34996     hideMenu : function(){
34997         // do nothing
34998     }
34999 });/*
35000  * Based on:
35001  * Ext JS Library 1.1.1
35002  * Copyright(c) 2006-2007, Ext JS, LLC.
35003  *
35004  * Originally Released Under LGPL - original licence link has changed is not relivant.
35005  *
35006  * Fork - LGPL
35007  * <script type="text/javascript">
35008  */
35009  
35010 /**
35011  * @class Roo.menu.Adapter
35012  * @extends Roo.menu.BaseItem
35013  * 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.
35014  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35015  * @constructor
35016  * Creates a new Adapter
35017  * @param {Object} config Configuration options
35018  */
35019 Roo.menu.Adapter = function(component, config){
35020     Roo.menu.Adapter.superclass.constructor.call(this, config);
35021     this.component = component;
35022 };
35023 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35024     // private
35025     canActivate : true,
35026
35027     // private
35028     onRender : function(container, position){
35029         this.component.render(container);
35030         this.el = this.component.getEl();
35031     },
35032
35033     // private
35034     activate : function(){
35035         if(this.disabled){
35036             return false;
35037         }
35038         this.component.focus();
35039         this.fireEvent("activate", this);
35040         return true;
35041     },
35042
35043     // private
35044     deactivate : function(){
35045         this.fireEvent("deactivate", this);
35046     },
35047
35048     // private
35049     disable : function(){
35050         this.component.disable();
35051         Roo.menu.Adapter.superclass.disable.call(this);
35052     },
35053
35054     // private
35055     enable : function(){
35056         this.component.enable();
35057         Roo.menu.Adapter.superclass.enable.call(this);
35058     }
35059 });/*
35060  * Based on:
35061  * Ext JS Library 1.1.1
35062  * Copyright(c) 2006-2007, Ext JS, LLC.
35063  *
35064  * Originally Released Under LGPL - original licence link has changed is not relivant.
35065  *
35066  * Fork - LGPL
35067  * <script type="text/javascript">
35068  */
35069
35070 /**
35071  * @class Roo.menu.TextItem
35072  * @extends Roo.menu.BaseItem
35073  * Adds a static text string to a menu, usually used as either a heading or group separator.
35074  * Note: old style constructor with text is still supported.
35075  * 
35076  * @constructor
35077  * Creates a new TextItem
35078  * @param {Object} cfg Configuration
35079  */
35080 Roo.menu.TextItem = function(cfg){
35081     if (typeof(cfg) == 'string') {
35082         this.text = cfg;
35083     } else {
35084         Roo.apply(this,cfg);
35085     }
35086     
35087     Roo.menu.TextItem.superclass.constructor.call(this);
35088 };
35089
35090 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35091     /**
35092      * @cfg {Boolean} text Text to show on item.
35093      */
35094     text : '',
35095     
35096     /**
35097      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35098      */
35099     hideOnClick : false,
35100     /**
35101      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35102      */
35103     itemCls : "x-menu-text",
35104
35105     // private
35106     onRender : function(){
35107         var s = document.createElement("span");
35108         s.className = this.itemCls;
35109         s.innerHTML = this.text;
35110         this.el = s;
35111         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35112     }
35113 });/*
35114  * Based on:
35115  * Ext JS Library 1.1.1
35116  * Copyright(c) 2006-2007, Ext JS, LLC.
35117  *
35118  * Originally Released Under LGPL - original licence link has changed is not relivant.
35119  *
35120  * Fork - LGPL
35121  * <script type="text/javascript">
35122  */
35123
35124 /**
35125  * @class Roo.menu.Separator
35126  * @extends Roo.menu.BaseItem
35127  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35128  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35129  * @constructor
35130  * @param {Object} config Configuration options
35131  */
35132 Roo.menu.Separator = function(config){
35133     Roo.menu.Separator.superclass.constructor.call(this, config);
35134 };
35135
35136 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35137     /**
35138      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35139      */
35140     itemCls : "x-menu-sep",
35141     /**
35142      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35143      */
35144     hideOnClick : false,
35145
35146     // private
35147     onRender : function(li){
35148         var s = document.createElement("span");
35149         s.className = this.itemCls;
35150         s.innerHTML = "&#160;";
35151         this.el = s;
35152         li.addClass("x-menu-sep-li");
35153         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35154     }
35155 });/*
35156  * Based on:
35157  * Ext JS Library 1.1.1
35158  * Copyright(c) 2006-2007, Ext JS, LLC.
35159  *
35160  * Originally Released Under LGPL - original licence link has changed is not relivant.
35161  *
35162  * Fork - LGPL
35163  * <script type="text/javascript">
35164  */
35165 /**
35166  * @class Roo.menu.Item
35167  * @extends Roo.menu.BaseItem
35168  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35169  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35170  * activation and click handling.
35171  * @constructor
35172  * Creates a new Item
35173  * @param {Object} config Configuration options
35174  */
35175 Roo.menu.Item = function(config){
35176     Roo.menu.Item.superclass.constructor.call(this, config);
35177     if(this.menu){
35178         this.menu = Roo.menu.MenuMgr.get(this.menu);
35179     }
35180 };
35181 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35182     
35183     /**
35184      * @cfg {String} text
35185      * The text to show on the menu item.
35186      */
35187     text: '',
35188      /**
35189      * @cfg {String} HTML to render in menu
35190      * The text to show on the menu item (HTML version).
35191      */
35192     html: '',
35193     /**
35194      * @cfg {String} icon
35195      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35196      */
35197     icon: undefined,
35198     /**
35199      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35200      */
35201     itemCls : "x-menu-item",
35202     /**
35203      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35204      */
35205     canActivate : true,
35206     /**
35207      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35208      */
35209     showDelay: 200,
35210     // doc'd in BaseItem
35211     hideDelay: 200,
35212
35213     // private
35214     ctype: "Roo.menu.Item",
35215     
35216     // private
35217     onRender : function(container, position){
35218         var el = document.createElement("a");
35219         el.hideFocus = true;
35220         el.unselectable = "on";
35221         el.href = this.href || "#";
35222         if(this.hrefTarget){
35223             el.target = this.hrefTarget;
35224         }
35225         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35226         
35227         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35228         
35229         el.innerHTML = String.format(
35230                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35231                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35232         this.el = el;
35233         Roo.menu.Item.superclass.onRender.call(this, container, position);
35234     },
35235
35236     /**
35237      * Sets the text to display in this menu item
35238      * @param {String} text The text to display
35239      * @param {Boolean} isHTML true to indicate text is pure html.
35240      */
35241     setText : function(text, isHTML){
35242         if (isHTML) {
35243             this.html = text;
35244         } else {
35245             this.text = text;
35246             this.html = '';
35247         }
35248         if(this.rendered){
35249             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35250      
35251             this.el.update(String.format(
35252                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35253                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35254             this.parentMenu.autoWidth();
35255         }
35256     },
35257
35258     // private
35259     handleClick : function(e){
35260         if(!this.href){ // if no link defined, stop the event automatically
35261             e.stopEvent();
35262         }
35263         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35264     },
35265
35266     // private
35267     activate : function(autoExpand){
35268         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35269             this.focus();
35270             if(autoExpand){
35271                 this.expandMenu();
35272             }
35273         }
35274         return true;
35275     },
35276
35277     // private
35278     shouldDeactivate : function(e){
35279         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35280             if(this.menu && this.menu.isVisible()){
35281                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35282             }
35283             return true;
35284         }
35285         return false;
35286     },
35287
35288     // private
35289     deactivate : function(){
35290         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35291         this.hideMenu();
35292     },
35293
35294     // private
35295     expandMenu : function(autoActivate){
35296         if(!this.disabled && this.menu){
35297             clearTimeout(this.hideTimer);
35298             delete this.hideTimer;
35299             if(!this.menu.isVisible() && !this.showTimer){
35300                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35301             }else if (this.menu.isVisible() && autoActivate){
35302                 this.menu.tryActivate(0, 1);
35303             }
35304         }
35305     },
35306
35307     // private
35308     deferExpand : function(autoActivate){
35309         delete this.showTimer;
35310         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35311         if(autoActivate){
35312             this.menu.tryActivate(0, 1);
35313         }
35314     },
35315
35316     // private
35317     hideMenu : function(){
35318         clearTimeout(this.showTimer);
35319         delete this.showTimer;
35320         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35321             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35322         }
35323     },
35324
35325     // private
35326     deferHide : function(){
35327         delete this.hideTimer;
35328         this.menu.hide();
35329     }
35330 });/*
35331  * Based on:
35332  * Ext JS Library 1.1.1
35333  * Copyright(c) 2006-2007, Ext JS, LLC.
35334  *
35335  * Originally Released Under LGPL - original licence link has changed is not relivant.
35336  *
35337  * Fork - LGPL
35338  * <script type="text/javascript">
35339  */
35340  
35341 /**
35342  * @class Roo.menu.CheckItem
35343  * @extends Roo.menu.Item
35344  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35345  * @constructor
35346  * Creates a new CheckItem
35347  * @param {Object} config Configuration options
35348  */
35349 Roo.menu.CheckItem = function(config){
35350     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35351     this.addEvents({
35352         /**
35353          * @event beforecheckchange
35354          * Fires before the checked value is set, providing an opportunity to cancel if needed
35355          * @param {Roo.menu.CheckItem} this
35356          * @param {Boolean} checked The new checked value that will be set
35357          */
35358         "beforecheckchange" : true,
35359         /**
35360          * @event checkchange
35361          * Fires after the checked value has been set
35362          * @param {Roo.menu.CheckItem} this
35363          * @param {Boolean} checked The checked value that was set
35364          */
35365         "checkchange" : true
35366     });
35367     if(this.checkHandler){
35368         this.on('checkchange', this.checkHandler, this.scope);
35369     }
35370 };
35371 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35372     /**
35373      * @cfg {String} group
35374      * All check items with the same group name will automatically be grouped into a single-select
35375      * radio button group (defaults to '')
35376      */
35377     /**
35378      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35379      */
35380     itemCls : "x-menu-item x-menu-check-item",
35381     /**
35382      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35383      */
35384     groupClass : "x-menu-group-item",
35385
35386     /**
35387      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35388      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35389      * initialized with checked = true will be rendered as checked.
35390      */
35391     checked: false,
35392
35393     // private
35394     ctype: "Roo.menu.CheckItem",
35395
35396     // private
35397     onRender : function(c){
35398         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35399         if(this.group){
35400             this.el.addClass(this.groupClass);
35401         }
35402         Roo.menu.MenuMgr.registerCheckable(this);
35403         if(this.checked){
35404             this.checked = false;
35405             this.setChecked(true, true);
35406         }
35407     },
35408
35409     // private
35410     destroy : function(){
35411         if(this.rendered){
35412             Roo.menu.MenuMgr.unregisterCheckable(this);
35413         }
35414         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35415     },
35416
35417     /**
35418      * Set the checked state of this item
35419      * @param {Boolean} checked The new checked value
35420      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35421      */
35422     setChecked : function(state, suppressEvent){
35423         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35424             if(this.container){
35425                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35426             }
35427             this.checked = state;
35428             if(suppressEvent !== true){
35429                 this.fireEvent("checkchange", this, state);
35430             }
35431         }
35432     },
35433
35434     // private
35435     handleClick : function(e){
35436        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35437            this.setChecked(!this.checked);
35438        }
35439        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35440     }
35441 });/*
35442  * Based on:
35443  * Ext JS Library 1.1.1
35444  * Copyright(c) 2006-2007, Ext JS, LLC.
35445  *
35446  * Originally Released Under LGPL - original licence link has changed is not relivant.
35447  *
35448  * Fork - LGPL
35449  * <script type="text/javascript">
35450  */
35451  
35452 /**
35453  * @class Roo.menu.DateItem
35454  * @extends Roo.menu.Adapter
35455  * A menu item that wraps the {@link Roo.DatPicker} component.
35456  * @constructor
35457  * Creates a new DateItem
35458  * @param {Object} config Configuration options
35459  */
35460 Roo.menu.DateItem = function(config){
35461     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35462     /** The Roo.DatePicker object @type Roo.DatePicker */
35463     this.picker = this.component;
35464     this.addEvents({select: true});
35465     
35466     this.picker.on("render", function(picker){
35467         picker.getEl().swallowEvent("click");
35468         picker.container.addClass("x-menu-date-item");
35469     });
35470
35471     this.picker.on("select", this.onSelect, this);
35472 };
35473
35474 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35475     // private
35476     onSelect : function(picker, date){
35477         this.fireEvent("select", this, date, picker);
35478         Roo.menu.DateItem.superclass.handleClick.call(this);
35479     }
35480 });/*
35481  * Based on:
35482  * Ext JS Library 1.1.1
35483  * Copyright(c) 2006-2007, Ext JS, LLC.
35484  *
35485  * Originally Released Under LGPL - original licence link has changed is not relivant.
35486  *
35487  * Fork - LGPL
35488  * <script type="text/javascript">
35489  */
35490  
35491 /**
35492  * @class Roo.menu.ColorItem
35493  * @extends Roo.menu.Adapter
35494  * A menu item that wraps the {@link Roo.ColorPalette} component.
35495  * @constructor
35496  * Creates a new ColorItem
35497  * @param {Object} config Configuration options
35498  */
35499 Roo.menu.ColorItem = function(config){
35500     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35501     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35502     this.palette = this.component;
35503     this.relayEvents(this.palette, ["select"]);
35504     if(this.selectHandler){
35505         this.on('select', this.selectHandler, this.scope);
35506     }
35507 };
35508 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35509  * Based on:
35510  * Ext JS Library 1.1.1
35511  * Copyright(c) 2006-2007, Ext JS, LLC.
35512  *
35513  * Originally Released Under LGPL - original licence link has changed is not relivant.
35514  *
35515  * Fork - LGPL
35516  * <script type="text/javascript">
35517  */
35518  
35519
35520 /**
35521  * @class Roo.menu.DateMenu
35522  * @extends Roo.menu.Menu
35523  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35524  * @constructor
35525  * Creates a new DateMenu
35526  * @param {Object} config Configuration options
35527  */
35528 Roo.menu.DateMenu = function(config){
35529     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35530     this.plain = true;
35531     var di = new Roo.menu.DateItem(config);
35532     this.add(di);
35533     /**
35534      * The {@link Roo.DatePicker} instance for this DateMenu
35535      * @type DatePicker
35536      */
35537     this.picker = di.picker;
35538     /**
35539      * @event select
35540      * @param {DatePicker} picker
35541      * @param {Date} date
35542      */
35543     this.relayEvents(di, ["select"]);
35544
35545     this.on('beforeshow', function(){
35546         if(this.picker){
35547             this.picker.hideMonthPicker(true);
35548         }
35549     }, this);
35550 };
35551 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35552     cls:'x-date-menu'
35553 });/*
35554  * Based on:
35555  * Ext JS Library 1.1.1
35556  * Copyright(c) 2006-2007, Ext JS, LLC.
35557  *
35558  * Originally Released Under LGPL - original licence link has changed is not relivant.
35559  *
35560  * Fork - LGPL
35561  * <script type="text/javascript">
35562  */
35563  
35564
35565 /**
35566  * @class Roo.menu.ColorMenu
35567  * @extends Roo.menu.Menu
35568  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35569  * @constructor
35570  * Creates a new ColorMenu
35571  * @param {Object} config Configuration options
35572  */
35573 Roo.menu.ColorMenu = function(config){
35574     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35575     this.plain = true;
35576     var ci = new Roo.menu.ColorItem(config);
35577     this.add(ci);
35578     /**
35579      * The {@link Roo.ColorPalette} instance for this ColorMenu
35580      * @type ColorPalette
35581      */
35582     this.palette = ci.palette;
35583     /**
35584      * @event select
35585      * @param {ColorPalette} palette
35586      * @param {String} color
35587      */
35588     this.relayEvents(ci, ["select"]);
35589 };
35590 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35591  * Based on:
35592  * Ext JS Library 1.1.1
35593  * Copyright(c) 2006-2007, Ext JS, LLC.
35594  *
35595  * Originally Released Under LGPL - original licence link has changed is not relivant.
35596  *
35597  * Fork - LGPL
35598  * <script type="text/javascript">
35599  */
35600  
35601 /**
35602  * @class Roo.form.Field
35603  * @extends Roo.BoxComponent
35604  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35605  * @constructor
35606  * Creates a new Field
35607  * @param {Object} config Configuration options
35608  */
35609 Roo.form.Field = function(config){
35610     Roo.form.Field.superclass.constructor.call(this, config);
35611 };
35612
35613 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35614     /**
35615      * @cfg {String} fieldLabel Label to use when rendering a form.
35616      */
35617        /**
35618      * @cfg {String} qtip Mouse over tip
35619      */
35620      
35621     /**
35622      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35623      */
35624     invalidClass : "x-form-invalid",
35625     /**
35626      * @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")
35627      */
35628     invalidText : "The value in this field is invalid",
35629     /**
35630      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35631      */
35632     focusClass : "x-form-focus",
35633     /**
35634      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35635       automatic validation (defaults to "keyup").
35636      */
35637     validationEvent : "keyup",
35638     /**
35639      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35640      */
35641     validateOnBlur : true,
35642     /**
35643      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35644      */
35645     validationDelay : 250,
35646     /**
35647      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35648      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35649      */
35650     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35651     /**
35652      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35653      */
35654     fieldClass : "x-form-field",
35655     /**
35656      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35657      *<pre>
35658 Value         Description
35659 -----------   ----------------------------------------------------------------------
35660 qtip          Display a quick tip when the user hovers over the field
35661 title         Display a default browser title attribute popup
35662 under         Add a block div beneath the field containing the error text
35663 side          Add an error icon to the right of the field with a popup on hover
35664 [element id]  Add the error text directly to the innerHTML of the specified element
35665 </pre>
35666      */
35667     msgTarget : 'qtip',
35668     /**
35669      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35670      */
35671     msgFx : 'normal',
35672
35673     /**
35674      * @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.
35675      */
35676     readOnly : false,
35677
35678     /**
35679      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35680      */
35681     disabled : false,
35682
35683     /**
35684      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35685      */
35686     inputType : undefined,
35687     
35688     /**
35689      * @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).
35690          */
35691         tabIndex : undefined,
35692         
35693     // private
35694     isFormField : true,
35695
35696     // private
35697     hasFocus : false,
35698     /**
35699      * @property {Roo.Element} fieldEl
35700      * Element Containing the rendered Field (with label etc.)
35701      */
35702     /**
35703      * @cfg {Mixed} value A value to initialize this field with.
35704      */
35705     value : undefined,
35706
35707     /**
35708      * @cfg {String} name The field's HTML name attribute.
35709      */
35710     /**
35711      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35712      */
35713
35714         // private ??
35715         initComponent : function(){
35716         Roo.form.Field.superclass.initComponent.call(this);
35717         this.addEvents({
35718             /**
35719              * @event focus
35720              * Fires when this field receives input focus.
35721              * @param {Roo.form.Field} this
35722              */
35723             focus : true,
35724             /**
35725              * @event blur
35726              * Fires when this field loses input focus.
35727              * @param {Roo.form.Field} this
35728              */
35729             blur : true,
35730             /**
35731              * @event specialkey
35732              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35733              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35734              * @param {Roo.form.Field} this
35735              * @param {Roo.EventObject} e The event object
35736              */
35737             specialkey : true,
35738             /**
35739              * @event change
35740              * Fires just before the field blurs if the field value has changed.
35741              * @param {Roo.form.Field} this
35742              * @param {Mixed} newValue The new value
35743              * @param {Mixed} oldValue The original value
35744              */
35745             change : true,
35746             /**
35747              * @event invalid
35748              * Fires after the field has been marked as invalid.
35749              * @param {Roo.form.Field} this
35750              * @param {String} msg The validation message
35751              */
35752             invalid : true,
35753             /**
35754              * @event valid
35755              * Fires after the field has been validated with no errors.
35756              * @param {Roo.form.Field} this
35757              */
35758             valid : true,
35759              /**
35760              * @event keyup
35761              * Fires after the key up
35762              * @param {Roo.form.Field} this
35763              * @param {Roo.EventObject}  e The event Object
35764              */
35765             keyup : true
35766         });
35767     },
35768
35769     /**
35770      * Returns the name attribute of the field if available
35771      * @return {String} name The field name
35772      */
35773     getName: function(){
35774          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35775     },
35776
35777     // private
35778     onRender : function(ct, position){
35779         Roo.form.Field.superclass.onRender.call(this, ct, position);
35780         if(!this.el){
35781             var cfg = this.getAutoCreate();
35782             if(!cfg.name){
35783                 cfg.name = this.name || this.id;
35784             }
35785             if(this.inputType){
35786                 cfg.type = this.inputType;
35787             }
35788             this.el = ct.createChild(cfg, position);
35789         }
35790         var type = this.el.dom.type;
35791         if(type){
35792             if(type == 'password'){
35793                 type = 'text';
35794             }
35795             this.el.addClass('x-form-'+type);
35796         }
35797         if(this.readOnly){
35798             this.el.dom.readOnly = true;
35799         }
35800         if(this.tabIndex !== undefined){
35801             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35802         }
35803
35804         this.el.addClass([this.fieldClass, this.cls]);
35805         this.initValue();
35806     },
35807
35808     /**
35809      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35810      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35811      * @return {Roo.form.Field} this
35812      */
35813     applyTo : function(target){
35814         this.allowDomMove = false;
35815         this.el = Roo.get(target);
35816         this.render(this.el.dom.parentNode);
35817         return this;
35818     },
35819
35820     // private
35821     initValue : function(){
35822         if(this.value !== undefined){
35823             this.setValue(this.value);
35824         }else if(this.el.dom.value.length > 0){
35825             this.setValue(this.el.dom.value);
35826         }
35827     },
35828
35829     /**
35830      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35831      */
35832     isDirty : function() {
35833         if(this.disabled) {
35834             return false;
35835         }
35836         return String(this.getValue()) !== String(this.originalValue);
35837     },
35838
35839     // private
35840     afterRender : function(){
35841         Roo.form.Field.superclass.afterRender.call(this);
35842         this.initEvents();
35843     },
35844
35845     // private
35846     fireKey : function(e){
35847         //Roo.log('field ' + e.getKey());
35848         if(e.isNavKeyPress()){
35849             this.fireEvent("specialkey", this, e);
35850         }
35851     },
35852
35853     /**
35854      * Resets the current field value to the originally loaded value and clears any validation messages
35855      */
35856     reset : function(){
35857         this.setValue(this.originalValue);
35858         this.clearInvalid();
35859     },
35860
35861     // private
35862     initEvents : function(){
35863         // safari killled keypress - so keydown is now used..
35864         this.el.on("keydown" , this.fireKey,  this);
35865         this.el.on("focus", this.onFocus,  this);
35866         this.el.on("blur", this.onBlur,  this);
35867         this.el.relayEvent('keyup', this);
35868
35869         // reference to original value for reset
35870         this.originalValue = this.getValue();
35871     },
35872
35873     // private
35874     onFocus : function(){
35875         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35876             this.el.addClass(this.focusClass);
35877         }
35878         if(!this.hasFocus){
35879             this.hasFocus = true;
35880             this.startValue = this.getValue();
35881             this.fireEvent("focus", this);
35882         }
35883     },
35884
35885     beforeBlur : Roo.emptyFn,
35886
35887     // private
35888     onBlur : function(){
35889         this.beforeBlur();
35890         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35891             this.el.removeClass(this.focusClass);
35892         }
35893         this.hasFocus = false;
35894         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35895             this.validate();
35896         }
35897         var v = this.getValue();
35898         if(String(v) !== String(this.startValue)){
35899             this.fireEvent('change', this, v, this.startValue);
35900         }
35901         this.fireEvent("blur", this);
35902     },
35903
35904     /**
35905      * Returns whether or not the field value is currently valid
35906      * @param {Boolean} preventMark True to disable marking the field invalid
35907      * @return {Boolean} True if the value is valid, else false
35908      */
35909     isValid : function(preventMark){
35910         if(this.disabled){
35911             return true;
35912         }
35913         var restore = this.preventMark;
35914         this.preventMark = preventMark === true;
35915         var v = this.validateValue(this.processValue(this.getRawValue()));
35916         this.preventMark = restore;
35917         return v;
35918     },
35919
35920     /**
35921      * Validates the field value
35922      * @return {Boolean} True if the value is valid, else false
35923      */
35924     validate : function(){
35925         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35926             this.clearInvalid();
35927             return true;
35928         }
35929         return false;
35930     },
35931
35932     processValue : function(value){
35933         return value;
35934     },
35935
35936     // private
35937     // Subclasses should provide the validation implementation by overriding this
35938     validateValue : function(value){
35939         return true;
35940     },
35941
35942     /**
35943      * Mark this field as invalid
35944      * @param {String} msg The validation message
35945      */
35946     markInvalid : function(msg){
35947         if(!this.rendered || this.preventMark){ // not rendered
35948             return;
35949         }
35950         this.el.addClass(this.invalidClass);
35951         msg = msg || this.invalidText;
35952         switch(this.msgTarget){
35953             case 'qtip':
35954                 this.el.dom.qtip = msg;
35955                 this.el.dom.qclass = 'x-form-invalid-tip';
35956                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35957                     Roo.QuickTips.enable();
35958                 }
35959                 break;
35960             case 'title':
35961                 this.el.dom.title = msg;
35962                 break;
35963             case 'under':
35964                 if(!this.errorEl){
35965                     var elp = this.el.findParent('.x-form-element', 5, true);
35966                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35967                     this.errorEl.setWidth(elp.getWidth(true)-20);
35968                 }
35969                 this.errorEl.update(msg);
35970                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35971                 break;
35972             case 'side':
35973                 if(!this.errorIcon){
35974                     var elp = this.el.findParent('.x-form-element', 5, true);
35975                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35976                 }
35977                 this.alignErrorIcon();
35978                 this.errorIcon.dom.qtip = msg;
35979                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35980                 this.errorIcon.show();
35981                 this.on('resize', this.alignErrorIcon, this);
35982                 break;
35983             default:
35984                 var t = Roo.getDom(this.msgTarget);
35985                 t.innerHTML = msg;
35986                 t.style.display = this.msgDisplay;
35987                 break;
35988         }
35989         this.fireEvent('invalid', this, msg);
35990     },
35991
35992     // private
35993     alignErrorIcon : function(){
35994         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35995     },
35996
35997     /**
35998      * Clear any invalid styles/messages for this field
35999      */
36000     clearInvalid : function(){
36001         if(!this.rendered || this.preventMark){ // not rendered
36002             return;
36003         }
36004         this.el.removeClass(this.invalidClass);
36005         switch(this.msgTarget){
36006             case 'qtip':
36007                 this.el.dom.qtip = '';
36008                 break;
36009             case 'title':
36010                 this.el.dom.title = '';
36011                 break;
36012             case 'under':
36013                 if(this.errorEl){
36014                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36015                 }
36016                 break;
36017             case 'side':
36018                 if(this.errorIcon){
36019                     this.errorIcon.dom.qtip = '';
36020                     this.errorIcon.hide();
36021                     this.un('resize', this.alignErrorIcon, this);
36022                 }
36023                 break;
36024             default:
36025                 var t = Roo.getDom(this.msgTarget);
36026                 t.innerHTML = '';
36027                 t.style.display = 'none';
36028                 break;
36029         }
36030         this.fireEvent('valid', this);
36031     },
36032
36033     /**
36034      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36035      * @return {Mixed} value The field value
36036      */
36037     getRawValue : function(){
36038         var v = this.el.getValue();
36039         if(v === this.emptyText){
36040             v = '';
36041         }
36042         return v;
36043     },
36044
36045     /**
36046      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36047      * @return {Mixed} value The field value
36048      */
36049     getValue : function(){
36050         var v = this.el.getValue();
36051         if(v === this.emptyText || v === undefined){
36052             v = '';
36053         }
36054         return v;
36055     },
36056
36057     /**
36058      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36059      * @param {Mixed} value The value to set
36060      */
36061     setRawValue : function(v){
36062         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36063     },
36064
36065     /**
36066      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36067      * @param {Mixed} value The value to set
36068      */
36069     setValue : function(v){
36070         this.value = v;
36071         if(this.rendered){
36072             this.el.dom.value = (v === null || v === undefined ? '' : v);
36073              this.validate();
36074         }
36075     },
36076
36077     adjustSize : function(w, h){
36078         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36079         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36080         return s;
36081     },
36082
36083     adjustWidth : function(tag, w){
36084         tag = tag.toLowerCase();
36085         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36086             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36087                 if(tag == 'input'){
36088                     return w + 2;
36089                 }
36090                 if(tag = 'textarea'){
36091                     return w-2;
36092                 }
36093             }else if(Roo.isOpera){
36094                 if(tag == 'input'){
36095                     return w + 2;
36096                 }
36097                 if(tag = 'textarea'){
36098                     return w-2;
36099                 }
36100             }
36101         }
36102         return w;
36103     }
36104 });
36105
36106
36107 // anything other than normal should be considered experimental
36108 Roo.form.Field.msgFx = {
36109     normal : {
36110         show: function(msgEl, f){
36111             msgEl.setDisplayed('block');
36112         },
36113
36114         hide : function(msgEl, f){
36115             msgEl.setDisplayed(false).update('');
36116         }
36117     },
36118
36119     slide : {
36120         show: function(msgEl, f){
36121             msgEl.slideIn('t', {stopFx:true});
36122         },
36123
36124         hide : function(msgEl, f){
36125             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36126         }
36127     },
36128
36129     slideRight : {
36130         show: function(msgEl, f){
36131             msgEl.fixDisplay();
36132             msgEl.alignTo(f.el, 'tl-tr');
36133             msgEl.slideIn('l', {stopFx:true});
36134         },
36135
36136         hide : function(msgEl, f){
36137             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36138         }
36139     }
36140 };/*
36141  * Based on:
36142  * Ext JS Library 1.1.1
36143  * Copyright(c) 2006-2007, Ext JS, LLC.
36144  *
36145  * Originally Released Under LGPL - original licence link has changed is not relivant.
36146  *
36147  * Fork - LGPL
36148  * <script type="text/javascript">
36149  */
36150  
36151
36152 /**
36153  * @class Roo.form.TextField
36154  * @extends Roo.form.Field
36155  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36156  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36157  * @constructor
36158  * Creates a new TextField
36159  * @param {Object} config Configuration options
36160  */
36161 Roo.form.TextField = function(config){
36162     Roo.form.TextField.superclass.constructor.call(this, config);
36163     this.addEvents({
36164         /**
36165          * @event autosize
36166          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36167          * according to the default logic, but this event provides a hook for the developer to apply additional
36168          * logic at runtime to resize the field if needed.
36169              * @param {Roo.form.Field} this This text field
36170              * @param {Number} width The new field width
36171              */
36172         autosize : true
36173     });
36174 };
36175
36176 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36177     /**
36178      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36179      */
36180     grow : false,
36181     /**
36182      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36183      */
36184     growMin : 30,
36185     /**
36186      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36187      */
36188     growMax : 800,
36189     /**
36190      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36191      */
36192     vtype : null,
36193     /**
36194      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36195      */
36196     maskRe : null,
36197     /**
36198      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36199      */
36200     disableKeyFilter : false,
36201     /**
36202      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36203      */
36204     allowBlank : true,
36205     /**
36206      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36207      */
36208     minLength : 0,
36209     /**
36210      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36211      */
36212     maxLength : Number.MAX_VALUE,
36213     /**
36214      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36215      */
36216     minLengthText : "The minimum length for this field is {0}",
36217     /**
36218      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36219      */
36220     maxLengthText : "The maximum length for this field is {0}",
36221     /**
36222      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36223      */
36224     selectOnFocus : false,
36225     /**
36226      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36227      */
36228     blankText : "This field is required",
36229     /**
36230      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36231      * If available, this function will be called only after the basic validators all return true, and will be passed the
36232      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36233      */
36234     validator : null,
36235     /**
36236      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36237      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36238      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36239      */
36240     regex : null,
36241     /**
36242      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36243      */
36244     regexText : "",
36245     /**
36246      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36247      */
36248     emptyText : null,
36249     /**
36250      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36251      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36252      */
36253     emptyClass : 'x-form-empty-field',
36254
36255     // private
36256     initEvents : function(){
36257         Roo.form.TextField.superclass.initEvents.call(this);
36258         if(this.validationEvent == 'keyup'){
36259             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36260             this.el.on('keyup', this.filterValidation, this);
36261         }
36262         else if(this.validationEvent !== false){
36263             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36264         }
36265         if(this.selectOnFocus || this.emptyText){
36266             this.on("focus", this.preFocus, this);
36267             if(this.emptyText){
36268                 this.on('blur', this.postBlur, this);
36269                 this.applyEmptyText();
36270             }
36271         }
36272         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36273             this.el.on("keypress", this.filterKeys, this);
36274         }
36275         if(this.grow){
36276             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36277             this.el.on("click", this.autoSize,  this);
36278         }
36279     },
36280
36281     processValue : function(value){
36282         if(this.stripCharsRe){
36283             var newValue = value.replace(this.stripCharsRe, '');
36284             if(newValue !== value){
36285                 this.setRawValue(newValue);
36286                 return newValue;
36287             }
36288         }
36289         return value;
36290     },
36291
36292     filterValidation : function(e){
36293         if(!e.isNavKeyPress()){
36294             this.validationTask.delay(this.validationDelay);
36295         }
36296     },
36297
36298     // private
36299     onKeyUp : function(e){
36300         if(!e.isNavKeyPress()){
36301             this.autoSize();
36302         }
36303     },
36304
36305     /**
36306      * Resets the current field value to the originally-loaded value and clears any validation messages.
36307      * Also adds emptyText and emptyClass if the original value was blank.
36308      */
36309     reset : function(){
36310         Roo.form.TextField.superclass.reset.call(this);
36311         this.applyEmptyText();
36312     },
36313
36314     applyEmptyText : function(){
36315         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36316             this.setRawValue(this.emptyText);
36317             this.el.addClass(this.emptyClass);
36318         }
36319     },
36320
36321     // private
36322     preFocus : function(){
36323         if(this.emptyText){
36324             if(this.el.dom.value == this.emptyText){
36325                 this.setRawValue('');
36326             }
36327             this.el.removeClass(this.emptyClass);
36328         }
36329         if(this.selectOnFocus){
36330             this.el.dom.select();
36331         }
36332     },
36333
36334     // private
36335     postBlur : function(){
36336         this.applyEmptyText();
36337     },
36338
36339     // private
36340     filterKeys : function(e){
36341         var k = e.getKey();
36342         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36343             return;
36344         }
36345         var c = e.getCharCode(), cc = String.fromCharCode(c);
36346         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36347             return;
36348         }
36349         if(!this.maskRe.test(cc)){
36350             e.stopEvent();
36351         }
36352     },
36353
36354     setValue : function(v){
36355         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36356             this.el.removeClass(this.emptyClass);
36357         }
36358         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36359         this.applyEmptyText();
36360         this.autoSize();
36361     },
36362
36363     /**
36364      * Validates a value according to the field's validation rules and marks the field as invalid
36365      * if the validation fails
36366      * @param {Mixed} value The value to validate
36367      * @return {Boolean} True if the value is valid, else false
36368      */
36369     validateValue : function(value){
36370         if(value.length < 1 || value === this.emptyText){ // if it's blank
36371              if(this.allowBlank){
36372                 this.clearInvalid();
36373                 return true;
36374              }else{
36375                 this.markInvalid(this.blankText);
36376                 return false;
36377              }
36378         }
36379         if(value.length < this.minLength){
36380             this.markInvalid(String.format(this.minLengthText, this.minLength));
36381             return false;
36382         }
36383         if(value.length > this.maxLength){
36384             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36385             return false;
36386         }
36387         if(this.vtype){
36388             var vt = Roo.form.VTypes;
36389             if(!vt[this.vtype](value, this)){
36390                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36391                 return false;
36392             }
36393         }
36394         if(typeof this.validator == "function"){
36395             var msg = this.validator(value);
36396             if(msg !== true){
36397                 this.markInvalid(msg);
36398                 return false;
36399             }
36400         }
36401         if(this.regex && !this.regex.test(value)){
36402             this.markInvalid(this.regexText);
36403             return false;
36404         }
36405         return true;
36406     },
36407
36408     /**
36409      * Selects text in this field
36410      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36411      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36412      */
36413     selectText : function(start, end){
36414         var v = this.getRawValue();
36415         if(v.length > 0){
36416             start = start === undefined ? 0 : start;
36417             end = end === undefined ? v.length : end;
36418             var d = this.el.dom;
36419             if(d.setSelectionRange){
36420                 d.setSelectionRange(start, end);
36421             }else if(d.createTextRange){
36422                 var range = d.createTextRange();
36423                 range.moveStart("character", start);
36424                 range.moveEnd("character", v.length-end);
36425                 range.select();
36426             }
36427         }
36428     },
36429
36430     /**
36431      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36432      * This only takes effect if grow = true, and fires the autosize event.
36433      */
36434     autoSize : function(){
36435         if(!this.grow || !this.rendered){
36436             return;
36437         }
36438         if(!this.metrics){
36439             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36440         }
36441         var el = this.el;
36442         var v = el.dom.value;
36443         var d = document.createElement('div');
36444         d.appendChild(document.createTextNode(v));
36445         v = d.innerHTML;
36446         d = null;
36447         v += "&#160;";
36448         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36449         this.el.setWidth(w);
36450         this.fireEvent("autosize", this, w);
36451     }
36452 });/*
36453  * Based on:
36454  * Ext JS Library 1.1.1
36455  * Copyright(c) 2006-2007, Ext JS, LLC.
36456  *
36457  * Originally Released Under LGPL - original licence link has changed is not relivant.
36458  *
36459  * Fork - LGPL
36460  * <script type="text/javascript">
36461  */
36462  
36463 /**
36464  * @class Roo.form.Hidden
36465  * @extends Roo.form.TextField
36466  * Simple Hidden element used on forms 
36467  * 
36468  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36469  * 
36470  * @constructor
36471  * Creates a new Hidden form element.
36472  * @param {Object} config Configuration options
36473  */
36474
36475
36476
36477 // easy hidden field...
36478 Roo.form.Hidden = function(config){
36479     Roo.form.Hidden.superclass.constructor.call(this, config);
36480 };
36481   
36482 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36483     fieldLabel:      '',
36484     inputType:      'hidden',
36485     width:          50,
36486     allowBlank:     true,
36487     labelSeparator: '',
36488     hidden:         true,
36489     itemCls :       'x-form-item-display-none'
36490
36491
36492 });
36493
36494
36495 /*
36496  * Based on:
36497  * Ext JS Library 1.1.1
36498  * Copyright(c) 2006-2007, Ext JS, LLC.
36499  *
36500  * Originally Released Under LGPL - original licence link has changed is not relivant.
36501  *
36502  * Fork - LGPL
36503  * <script type="text/javascript">
36504  */
36505  
36506 /**
36507  * @class Roo.form.TriggerField
36508  * @extends Roo.form.TextField
36509  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36510  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36511  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36512  * for which you can provide a custom implementation.  For example:
36513  * <pre><code>
36514 var trigger = new Roo.form.TriggerField();
36515 trigger.onTriggerClick = myTriggerFn;
36516 trigger.applyTo('my-field');
36517 </code></pre>
36518  *
36519  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36520  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36521  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36522  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36523  * @constructor
36524  * Create a new TriggerField.
36525  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36526  * to the base TextField)
36527  */
36528 Roo.form.TriggerField = function(config){
36529     this.mimicing = false;
36530     Roo.form.TriggerField.superclass.constructor.call(this, config);
36531 };
36532
36533 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36534     /**
36535      * @cfg {String} triggerClass A CSS class to apply to the trigger
36536      */
36537     /**
36538      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36539      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36540      */
36541     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36542     /**
36543      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36544      */
36545     hideTrigger:false,
36546
36547     /** @cfg {Boolean} grow @hide */
36548     /** @cfg {Number} growMin @hide */
36549     /** @cfg {Number} growMax @hide */
36550
36551     /**
36552      * @hide 
36553      * @method
36554      */
36555     autoSize: Roo.emptyFn,
36556     // private
36557     monitorTab : true,
36558     // private
36559     deferHeight : true,
36560
36561     
36562     actionMode : 'wrap',
36563     // private
36564     onResize : function(w, h){
36565         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36566         if(typeof w == 'number'){
36567             var x = w - this.trigger.getWidth();
36568             this.el.setWidth(this.adjustWidth('input', x));
36569             this.trigger.setStyle('left', x+'px');
36570         }
36571     },
36572
36573     // private
36574     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36575
36576     // private
36577     getResizeEl : function(){
36578         return this.wrap;
36579     },
36580
36581     // private
36582     getPositionEl : function(){
36583         return this.wrap;
36584     },
36585
36586     // private
36587     alignErrorIcon : function(){
36588         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36589     },
36590
36591     // private
36592     onRender : function(ct, position){
36593         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36594         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36595         this.trigger = this.wrap.createChild(this.triggerConfig ||
36596                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36597         if(this.hideTrigger){
36598             this.trigger.setDisplayed(false);
36599         }
36600         this.initTrigger();
36601         if(!this.width){
36602             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36603         }
36604     },
36605
36606     // private
36607     initTrigger : function(){
36608         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36609         this.trigger.addClassOnOver('x-form-trigger-over');
36610         this.trigger.addClassOnClick('x-form-trigger-click');
36611     },
36612
36613     // private
36614     onDestroy : function(){
36615         if(this.trigger){
36616             this.trigger.removeAllListeners();
36617             this.trigger.remove();
36618         }
36619         if(this.wrap){
36620             this.wrap.remove();
36621         }
36622         Roo.form.TriggerField.superclass.onDestroy.call(this);
36623     },
36624
36625     // private
36626     onFocus : function(){
36627         Roo.form.TriggerField.superclass.onFocus.call(this);
36628         if(!this.mimicing){
36629             this.wrap.addClass('x-trigger-wrap-focus');
36630             this.mimicing = true;
36631             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36632             if(this.monitorTab){
36633                 this.el.on("keydown", this.checkTab, this);
36634             }
36635         }
36636     },
36637
36638     // private
36639     checkTab : function(e){
36640         if(e.getKey() == e.TAB){
36641             this.triggerBlur();
36642         }
36643     },
36644
36645     // private
36646     onBlur : function(){
36647         // do nothing
36648     },
36649
36650     // private
36651     mimicBlur : function(e, t){
36652         if(!this.wrap.contains(t) && this.validateBlur()){
36653             this.triggerBlur();
36654         }
36655     },
36656
36657     // private
36658     triggerBlur : function(){
36659         this.mimicing = false;
36660         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36661         if(this.monitorTab){
36662             this.el.un("keydown", this.checkTab, this);
36663         }
36664         this.wrap.removeClass('x-trigger-wrap-focus');
36665         Roo.form.TriggerField.superclass.onBlur.call(this);
36666     },
36667
36668     // private
36669     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36670     validateBlur : function(e, t){
36671         return true;
36672     },
36673
36674     // private
36675     onDisable : function(){
36676         Roo.form.TriggerField.superclass.onDisable.call(this);
36677         if(this.wrap){
36678             this.wrap.addClass('x-item-disabled');
36679         }
36680     },
36681
36682     // private
36683     onEnable : function(){
36684         Roo.form.TriggerField.superclass.onEnable.call(this);
36685         if(this.wrap){
36686             this.wrap.removeClass('x-item-disabled');
36687         }
36688     },
36689
36690     // private
36691     onShow : function(){
36692         var ae = this.getActionEl();
36693         
36694         if(ae){
36695             ae.dom.style.display = '';
36696             ae.dom.style.visibility = 'visible';
36697         }
36698     },
36699
36700     // private
36701     
36702     onHide : function(){
36703         var ae = this.getActionEl();
36704         ae.dom.style.display = 'none';
36705     },
36706
36707     /**
36708      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36709      * by an implementing function.
36710      * @method
36711      * @param {EventObject} e
36712      */
36713     onTriggerClick : Roo.emptyFn
36714 });
36715
36716 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36717 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36718 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36719 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36720     initComponent : function(){
36721         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36722
36723         this.triggerConfig = {
36724             tag:'span', cls:'x-form-twin-triggers', cn:[
36725             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36726             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36727         ]};
36728     },
36729
36730     getTrigger : function(index){
36731         return this.triggers[index];
36732     },
36733
36734     initTrigger : function(){
36735         var ts = this.trigger.select('.x-form-trigger', true);
36736         this.wrap.setStyle('overflow', 'hidden');
36737         var triggerField = this;
36738         ts.each(function(t, all, index){
36739             t.hide = function(){
36740                 var w = triggerField.wrap.getWidth();
36741                 this.dom.style.display = 'none';
36742                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36743             };
36744             t.show = function(){
36745                 var w = triggerField.wrap.getWidth();
36746                 this.dom.style.display = '';
36747                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36748             };
36749             var triggerIndex = 'Trigger'+(index+1);
36750
36751             if(this['hide'+triggerIndex]){
36752                 t.dom.style.display = 'none';
36753             }
36754             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36755             t.addClassOnOver('x-form-trigger-over');
36756             t.addClassOnClick('x-form-trigger-click');
36757         }, this);
36758         this.triggers = ts.elements;
36759     },
36760
36761     onTrigger1Click : Roo.emptyFn,
36762     onTrigger2Click : Roo.emptyFn
36763 });/*
36764  * Based on:
36765  * Ext JS Library 1.1.1
36766  * Copyright(c) 2006-2007, Ext JS, LLC.
36767  *
36768  * Originally Released Under LGPL - original licence link has changed is not relivant.
36769  *
36770  * Fork - LGPL
36771  * <script type="text/javascript">
36772  */
36773  
36774 /**
36775  * @class Roo.form.TextArea
36776  * @extends Roo.form.TextField
36777  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36778  * support for auto-sizing.
36779  * @constructor
36780  * Creates a new TextArea
36781  * @param {Object} config Configuration options
36782  */
36783 Roo.form.TextArea = function(config){
36784     Roo.form.TextArea.superclass.constructor.call(this, config);
36785     // these are provided exchanges for backwards compat
36786     // minHeight/maxHeight were replaced by growMin/growMax to be
36787     // compatible with TextField growing config values
36788     if(this.minHeight !== undefined){
36789         this.growMin = this.minHeight;
36790     }
36791     if(this.maxHeight !== undefined){
36792         this.growMax = this.maxHeight;
36793     }
36794 };
36795
36796 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36797     /**
36798      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36799      */
36800     growMin : 60,
36801     /**
36802      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36803      */
36804     growMax: 1000,
36805     /**
36806      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36807      * in the field (equivalent to setting overflow: hidden, defaults to false)
36808      */
36809     preventScrollbars: false,
36810     /**
36811      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36812      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36813      */
36814
36815     // private
36816     onRender : function(ct, position){
36817         if(!this.el){
36818             this.defaultAutoCreate = {
36819                 tag: "textarea",
36820                 style:"width:300px;height:60px;",
36821                 autocomplete: "off"
36822             };
36823         }
36824         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36825         if(this.grow){
36826             this.textSizeEl = Roo.DomHelper.append(document.body, {
36827                 tag: "pre", cls: "x-form-grow-sizer"
36828             });
36829             if(this.preventScrollbars){
36830                 this.el.setStyle("overflow", "hidden");
36831             }
36832             this.el.setHeight(this.growMin);
36833         }
36834     },
36835
36836     onDestroy : function(){
36837         if(this.textSizeEl){
36838             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36839         }
36840         Roo.form.TextArea.superclass.onDestroy.call(this);
36841     },
36842
36843     // private
36844     onKeyUp : function(e){
36845         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36846             this.autoSize();
36847         }
36848     },
36849
36850     /**
36851      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36852      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36853      */
36854     autoSize : function(){
36855         if(!this.grow || !this.textSizeEl){
36856             return;
36857         }
36858         var el = this.el;
36859         var v = el.dom.value;
36860         var ts = this.textSizeEl;
36861
36862         ts.innerHTML = '';
36863         ts.appendChild(document.createTextNode(v));
36864         v = ts.innerHTML;
36865
36866         Roo.fly(ts).setWidth(this.el.getWidth());
36867         if(v.length < 1){
36868             v = "&#160;&#160;";
36869         }else{
36870             if(Roo.isIE){
36871                 v = v.replace(/\n/g, '<p>&#160;</p>');
36872             }
36873             v += "&#160;\n&#160;";
36874         }
36875         ts.innerHTML = v;
36876         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36877         if(h != this.lastHeight){
36878             this.lastHeight = h;
36879             this.el.setHeight(h);
36880             this.fireEvent("autosize", this, h);
36881         }
36882     }
36883 });/*
36884  * Based on:
36885  * Ext JS Library 1.1.1
36886  * Copyright(c) 2006-2007, Ext JS, LLC.
36887  *
36888  * Originally Released Under LGPL - original licence link has changed is not relivant.
36889  *
36890  * Fork - LGPL
36891  * <script type="text/javascript">
36892  */
36893  
36894
36895 /**
36896  * @class Roo.form.NumberField
36897  * @extends Roo.form.TextField
36898  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36899  * @constructor
36900  * Creates a new NumberField
36901  * @param {Object} config Configuration options
36902  */
36903 Roo.form.NumberField = function(config){
36904     Roo.form.NumberField.superclass.constructor.call(this, config);
36905 };
36906
36907 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36908     /**
36909      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36910      */
36911     fieldClass: "x-form-field x-form-num-field",
36912     /**
36913      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36914      */
36915     allowDecimals : true,
36916     /**
36917      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36918      */
36919     decimalSeparator : ".",
36920     /**
36921      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36922      */
36923     decimalPrecision : 2,
36924     /**
36925      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36926      */
36927     allowNegative : true,
36928     /**
36929      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36930      */
36931     minValue : Number.NEGATIVE_INFINITY,
36932     /**
36933      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36934      */
36935     maxValue : Number.MAX_VALUE,
36936     /**
36937      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36938      */
36939     minText : "The minimum value for this field is {0}",
36940     /**
36941      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36942      */
36943     maxText : "The maximum value for this field is {0}",
36944     /**
36945      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36946      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36947      */
36948     nanText : "{0} is not a valid number",
36949
36950     // private
36951     initEvents : function(){
36952         Roo.form.NumberField.superclass.initEvents.call(this);
36953         var allowed = "0123456789";
36954         if(this.allowDecimals){
36955             allowed += this.decimalSeparator;
36956         }
36957         if(this.allowNegative){
36958             allowed += "-";
36959         }
36960         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36961         var keyPress = function(e){
36962             var k = e.getKey();
36963             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36964                 return;
36965             }
36966             var c = e.getCharCode();
36967             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36968                 e.stopEvent();
36969             }
36970         };
36971         this.el.on("keypress", keyPress, this);
36972     },
36973
36974     // private
36975     validateValue : function(value){
36976         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36977             return false;
36978         }
36979         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36980              return true;
36981         }
36982         var num = this.parseValue(value);
36983         if(isNaN(num)){
36984             this.markInvalid(String.format(this.nanText, value));
36985             return false;
36986         }
36987         if(num < this.minValue){
36988             this.markInvalid(String.format(this.minText, this.minValue));
36989             return false;
36990         }
36991         if(num > this.maxValue){
36992             this.markInvalid(String.format(this.maxText, this.maxValue));
36993             return false;
36994         }
36995         return true;
36996     },
36997
36998     getValue : function(){
36999         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37000     },
37001
37002     // private
37003     parseValue : function(value){
37004         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37005         return isNaN(value) ? '' : value;
37006     },
37007
37008     // private
37009     fixPrecision : function(value){
37010         var nan = isNaN(value);
37011         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37012             return nan ? '' : value;
37013         }
37014         return parseFloat(value).toFixed(this.decimalPrecision);
37015     },
37016
37017     setValue : function(v){
37018         v = this.fixPrecision(v);
37019         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37020     },
37021
37022     // private
37023     decimalPrecisionFcn : function(v){
37024         return Math.floor(v);
37025     },
37026
37027     beforeBlur : function(){
37028         var v = this.parseValue(this.getRawValue());
37029         if(v){
37030             this.setValue(v);
37031         }
37032     }
37033 });/*
37034  * Based on:
37035  * Ext JS Library 1.1.1
37036  * Copyright(c) 2006-2007, Ext JS, LLC.
37037  *
37038  * Originally Released Under LGPL - original licence link has changed is not relivant.
37039  *
37040  * Fork - LGPL
37041  * <script type="text/javascript">
37042  */
37043  
37044 /**
37045  * @class Roo.form.DateField
37046  * @extends Roo.form.TriggerField
37047  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37048 * @constructor
37049 * Create a new DateField
37050 * @param {Object} config
37051  */
37052 Roo.form.DateField = function(config){
37053     Roo.form.DateField.superclass.constructor.call(this, config);
37054     
37055       this.addEvents({
37056          
37057         /**
37058          * @event select
37059          * Fires when a date is selected
37060              * @param {Roo.form.DateField} combo This combo box
37061              * @param {Date} date The date selected
37062              */
37063         'select' : true
37064          
37065     });
37066     
37067     
37068     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37069     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37070     this.ddMatch = null;
37071     if(this.disabledDates){
37072         var dd = this.disabledDates;
37073         var re = "(?:";
37074         for(var i = 0; i < dd.length; i++){
37075             re += dd[i];
37076             if(i != dd.length-1) re += "|";
37077         }
37078         this.ddMatch = new RegExp(re + ")");
37079     }
37080 };
37081
37082 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37083     /**
37084      * @cfg {String} format
37085      * The default date format string which can be overriden for localization support.  The format must be
37086      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37087      */
37088     format : "m/d/y",
37089     /**
37090      * @cfg {String} altFormats
37091      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37092      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37093      */
37094     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37095     /**
37096      * @cfg {Array} disabledDays
37097      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37098      */
37099     disabledDays : null,
37100     /**
37101      * @cfg {String} disabledDaysText
37102      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37103      */
37104     disabledDaysText : "Disabled",
37105     /**
37106      * @cfg {Array} disabledDates
37107      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37108      * expression so they are very powerful. Some examples:
37109      * <ul>
37110      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37111      * <li>["03/08", "09/16"] would disable those days for every year</li>
37112      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37113      * <li>["03/../2006"] would disable every day in March 2006</li>
37114      * <li>["^03"] would disable every day in every March</li>
37115      * </ul>
37116      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37117      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37118      */
37119     disabledDates : null,
37120     /**
37121      * @cfg {String} disabledDatesText
37122      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37123      */
37124     disabledDatesText : "Disabled",
37125     /**
37126      * @cfg {Date/String} minValue
37127      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37128      * valid format (defaults to null).
37129      */
37130     minValue : null,
37131     /**
37132      * @cfg {Date/String} maxValue
37133      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37134      * valid format (defaults to null).
37135      */
37136     maxValue : null,
37137     /**
37138      * @cfg {String} minText
37139      * The error text to display when the date in the cell is before minValue (defaults to
37140      * 'The date in this field must be after {minValue}').
37141      */
37142     minText : "The date in this field must be equal to or after {0}",
37143     /**
37144      * @cfg {String} maxText
37145      * The error text to display when the date in the cell is after maxValue (defaults to
37146      * 'The date in this field must be before {maxValue}').
37147      */
37148     maxText : "The date in this field must be equal to or before {0}",
37149     /**
37150      * @cfg {String} invalidText
37151      * The error text to display when the date in the field is invalid (defaults to
37152      * '{value} is not a valid date - it must be in the format {format}').
37153      */
37154     invalidText : "{0} is not a valid date - it must be in the format {1}",
37155     /**
37156      * @cfg {String} triggerClass
37157      * An additional CSS class used to style the trigger button.  The trigger will always get the
37158      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37159      * which displays a calendar icon).
37160      */
37161     triggerClass : 'x-form-date-trigger',
37162     
37163
37164     /**
37165      * @cfg {bool} useIso
37166      * if enabled, then the date field will use a hidden field to store the 
37167      * real value as iso formated date. default (false)
37168      */ 
37169     useIso : false,
37170     /**
37171      * @cfg {String/Object} autoCreate
37172      * A DomHelper element spec, or true for a default element spec (defaults to
37173      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37174      */ 
37175     // private
37176     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37177     
37178     // private
37179     hiddenField: false,
37180     
37181     onRender : function(ct, position)
37182     {
37183         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37184         if (this.useIso) {
37185             this.el.dom.removeAttribute('name'); 
37186             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37187                     'before', true);
37188             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37189             // prevent input submission
37190             this.hiddenName = this.name;
37191         }
37192             
37193             
37194     },
37195     
37196     // private
37197     validateValue : function(value)
37198     {
37199         value = this.formatDate(value);
37200         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37201             return false;
37202         }
37203         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37204              return true;
37205         }
37206         var svalue = value;
37207         value = this.parseDate(value);
37208         if(!value){
37209             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37210             return false;
37211         }
37212         var time = value.getTime();
37213         if(this.minValue && time < this.minValue.getTime()){
37214             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37215             return false;
37216         }
37217         if(this.maxValue && time > this.maxValue.getTime()){
37218             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37219             return false;
37220         }
37221         if(this.disabledDays){
37222             var day = value.getDay();
37223             for(var i = 0; i < this.disabledDays.length; i++) {
37224                 if(day === this.disabledDays[i]){
37225                     this.markInvalid(this.disabledDaysText);
37226                     return false;
37227                 }
37228             }
37229         }
37230         var fvalue = this.formatDate(value);
37231         if(this.ddMatch && this.ddMatch.test(fvalue)){
37232             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37233             return false;
37234         }
37235         return true;
37236     },
37237
37238     // private
37239     // Provides logic to override the default TriggerField.validateBlur which just returns true
37240     validateBlur : function(){
37241         return !this.menu || !this.menu.isVisible();
37242     },
37243
37244     /**
37245      * Returns the current date value of the date field.
37246      * @return {Date} The date value
37247      */
37248     getValue : function(){
37249         
37250         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37251     },
37252
37253     /**
37254      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37255      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37256      * (the default format used is "m/d/y").
37257      * <br />Usage:
37258      * <pre><code>
37259 //All of these calls set the same date value (May 4, 2006)
37260
37261 //Pass a date object:
37262 var dt = new Date('5/4/06');
37263 dateField.setValue(dt);
37264
37265 //Pass a date string (default format):
37266 dateField.setValue('5/4/06');
37267
37268 //Pass a date string (custom format):
37269 dateField.format = 'Y-m-d';
37270 dateField.setValue('2006-5-4');
37271 </code></pre>
37272      * @param {String/Date} date The date or valid date string
37273      */
37274     setValue : function(date){
37275         if (this.hiddenField) {
37276             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37277         }
37278         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37279     },
37280
37281     // private
37282     parseDate : function(value){
37283         if(!value || value instanceof Date){
37284             return value;
37285         }
37286         var v = Date.parseDate(value, this.format);
37287         if(!v && this.altFormats){
37288             if(!this.altFormatsArray){
37289                 this.altFormatsArray = this.altFormats.split("|");
37290             }
37291             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37292                 v = Date.parseDate(value, this.altFormatsArray[i]);
37293             }
37294         }
37295         return v;
37296     },
37297
37298     // private
37299     formatDate : function(date, fmt){
37300         return (!date || !(date instanceof Date)) ?
37301                date : date.dateFormat(fmt || this.format);
37302     },
37303
37304     // private
37305     menuListeners : {
37306         select: function(m, d){
37307             this.setValue(d);
37308             this.fireEvent('select', this, d);
37309         },
37310         show : function(){ // retain focus styling
37311             this.onFocus();
37312         },
37313         hide : function(){
37314             this.focus.defer(10, this);
37315             var ml = this.menuListeners;
37316             this.menu.un("select", ml.select,  this);
37317             this.menu.un("show", ml.show,  this);
37318             this.menu.un("hide", ml.hide,  this);
37319         }
37320     },
37321
37322     // private
37323     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37324     onTriggerClick : function(){
37325         if(this.disabled){
37326             return;
37327         }
37328         if(this.menu == null){
37329             this.menu = new Roo.menu.DateMenu();
37330         }
37331         Roo.apply(this.menu.picker,  {
37332             showClear: this.allowBlank,
37333             minDate : this.minValue,
37334             maxDate : this.maxValue,
37335             disabledDatesRE : this.ddMatch,
37336             disabledDatesText : this.disabledDatesText,
37337             disabledDays : this.disabledDays,
37338             disabledDaysText : this.disabledDaysText,
37339             format : this.format,
37340             minText : String.format(this.minText, this.formatDate(this.minValue)),
37341             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37342         });
37343         this.menu.on(Roo.apply({}, this.menuListeners, {
37344             scope:this
37345         }));
37346         this.menu.picker.setValue(this.getValue() || new Date());
37347         this.menu.show(this.el, "tl-bl?");
37348     },
37349
37350     beforeBlur : function(){
37351         var v = this.parseDate(this.getRawValue());
37352         if(v){
37353             this.setValue(v);
37354         }
37355     }
37356
37357     /** @cfg {Boolean} grow @hide */
37358     /** @cfg {Number} growMin @hide */
37359     /** @cfg {Number} growMax @hide */
37360     /**
37361      * @hide
37362      * @method autoSize
37363      */
37364 });/*
37365  * Based on:
37366  * Ext JS Library 1.1.1
37367  * Copyright(c) 2006-2007, Ext JS, LLC.
37368  *
37369  * Originally Released Under LGPL - original licence link has changed is not relivant.
37370  *
37371  * Fork - LGPL
37372  * <script type="text/javascript">
37373  */
37374  
37375
37376 /**
37377  * @class Roo.form.ComboBox
37378  * @extends Roo.form.TriggerField
37379  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37380  * @constructor
37381  * Create a new ComboBox.
37382  * @param {Object} config Configuration options
37383  */
37384 Roo.form.ComboBox = function(config){
37385     Roo.form.ComboBox.superclass.constructor.call(this, config);
37386     this.addEvents({
37387         /**
37388          * @event expand
37389          * Fires when the dropdown list is expanded
37390              * @param {Roo.form.ComboBox} combo This combo box
37391              */
37392         'expand' : true,
37393         /**
37394          * @event collapse
37395          * Fires when the dropdown list is collapsed
37396              * @param {Roo.form.ComboBox} combo This combo box
37397              */
37398         'collapse' : true,
37399         /**
37400          * @event beforeselect
37401          * Fires before a list item is selected. Return false to cancel the selection.
37402              * @param {Roo.form.ComboBox} combo This combo box
37403              * @param {Roo.data.Record} record The data record returned from the underlying store
37404              * @param {Number} index The index of the selected item in the dropdown list
37405              */
37406         'beforeselect' : true,
37407         /**
37408          * @event select
37409          * Fires when a list item is selected
37410              * @param {Roo.form.ComboBox} combo This combo box
37411              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37412              * @param {Number} index The index of the selected item in the dropdown list
37413              */
37414         'select' : true,
37415         /**
37416          * @event beforequery
37417          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37418          * The event object passed has these properties:
37419              * @param {Roo.form.ComboBox} combo This combo box
37420              * @param {String} query The query
37421              * @param {Boolean} forceAll true to force "all" query
37422              * @param {Boolean} cancel true to cancel the query
37423              * @param {Object} e The query event object
37424              */
37425         'beforequery': true,
37426          /**
37427          * @event add
37428          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37429              * @param {Roo.form.ComboBox} combo This combo box
37430              */
37431         'add' : true,
37432         /**
37433          * @event edit
37434          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37435              * @param {Roo.form.ComboBox} combo This combo box
37436              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37437              */
37438         'edit' : true
37439         
37440         
37441     });
37442     if(this.transform){
37443         this.allowDomMove = false;
37444         var s = Roo.getDom(this.transform);
37445         if(!this.hiddenName){
37446             this.hiddenName = s.name;
37447         }
37448         if(!this.store){
37449             this.mode = 'local';
37450             var d = [], opts = s.options;
37451             for(var i = 0, len = opts.length;i < len; i++){
37452                 var o = opts[i];
37453                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37454                 if(o.selected) {
37455                     this.value = value;
37456                 }
37457                 d.push([value, o.text]);
37458             }
37459             this.store = new Roo.data.SimpleStore({
37460                 'id': 0,
37461                 fields: ['value', 'text'],
37462                 data : d
37463             });
37464             this.valueField = 'value';
37465             this.displayField = 'text';
37466         }
37467         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37468         if(!this.lazyRender){
37469             this.target = true;
37470             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37471             s.parentNode.removeChild(s); // remove it
37472             this.render(this.el.parentNode);
37473         }else{
37474             s.parentNode.removeChild(s); // remove it
37475         }
37476
37477     }
37478     if (this.store) {
37479         this.store = Roo.factory(this.store, Roo.data);
37480     }
37481     
37482     this.selectedIndex = -1;
37483     if(this.mode == 'local'){
37484         if(config.queryDelay === undefined){
37485             this.queryDelay = 10;
37486         }
37487         if(config.minChars === undefined){
37488             this.minChars = 0;
37489         }
37490     }
37491 };
37492
37493 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37494     /**
37495      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37496      */
37497     /**
37498      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37499      * rendering into an Roo.Editor, defaults to false)
37500      */
37501     /**
37502      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37503      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37504      */
37505     /**
37506      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37507      */
37508     /**
37509      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37510      * the dropdown list (defaults to undefined, with no header element)
37511      */
37512
37513      /**
37514      * @cfg {String/Roo.Template} tpl The template to use to render the output
37515      */
37516      
37517     // private
37518     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37519     /**
37520      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37521      */
37522     listWidth: undefined,
37523     /**
37524      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37525      * mode = 'remote' or 'text' if mode = 'local')
37526      */
37527     displayField: undefined,
37528     /**
37529      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37530      * mode = 'remote' or 'value' if mode = 'local'). 
37531      * Note: use of a valueField requires the user make a selection
37532      * in order for a value to be mapped.
37533      */
37534     valueField: undefined,
37535     
37536     
37537     /**
37538      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37539      * field's data value (defaults to the underlying DOM element's name)
37540      */
37541     hiddenName: undefined,
37542     /**
37543      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37544      */
37545     listClass: '',
37546     /**
37547      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37548      */
37549     selectedClass: 'x-combo-selected',
37550     /**
37551      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37552      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37553      * which displays a downward arrow icon).
37554      */
37555     triggerClass : 'x-form-arrow-trigger',
37556     /**
37557      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37558      */
37559     shadow:'sides',
37560     /**
37561      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37562      * anchor positions (defaults to 'tl-bl')
37563      */
37564     listAlign: 'tl-bl?',
37565     /**
37566      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37567      */
37568     maxHeight: 300,
37569     /**
37570      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37571      * query specified by the allQuery config option (defaults to 'query')
37572      */
37573     triggerAction: 'query',
37574     /**
37575      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37576      * (defaults to 4, does not apply if editable = false)
37577      */
37578     minChars : 4,
37579     /**
37580      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37581      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37582      */
37583     typeAhead: false,
37584     /**
37585      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37586      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37587      */
37588     queryDelay: 500,
37589     /**
37590      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37591      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37592      */
37593     pageSize: 0,
37594     /**
37595      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37596      * when editable = true (defaults to false)
37597      */
37598     selectOnFocus:false,
37599     /**
37600      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37601      */
37602     queryParam: 'query',
37603     /**
37604      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37605      * when mode = 'remote' (defaults to 'Loading...')
37606      */
37607     loadingText: 'Loading...',
37608     /**
37609      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37610      */
37611     resizable: false,
37612     /**
37613      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37614      */
37615     handleHeight : 8,
37616     /**
37617      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37618      * traditional select (defaults to true)
37619      */
37620     editable: true,
37621     /**
37622      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37623      */
37624     allQuery: '',
37625     /**
37626      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37627      */
37628     mode: 'remote',
37629     /**
37630      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37631      * listWidth has a higher value)
37632      */
37633     minListWidth : 70,
37634     /**
37635      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37636      * allow the user to set arbitrary text into the field (defaults to false)
37637      */
37638     forceSelection:false,
37639     /**
37640      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37641      * if typeAhead = true (defaults to 250)
37642      */
37643     typeAheadDelay : 250,
37644     /**
37645      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37646      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37647      */
37648     valueNotFoundText : undefined,
37649     /**
37650      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37651      */
37652     blockFocus : false,
37653     
37654     /**
37655      * @cfg {Boolean} disableClear Disable showing of clear button.
37656      */
37657     disableClear : false,
37658     /**
37659      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37660      */
37661     alwaysQuery : false,
37662     
37663     //private
37664     addicon : false,
37665     editicon: false,
37666     
37667     // element that contains real text value.. (when hidden is used..)
37668      
37669     // private
37670     onRender : function(ct, position){
37671         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37672         if(this.hiddenName){
37673             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37674                     'before', true);
37675             this.hiddenField.value =
37676                 this.hiddenValue !== undefined ? this.hiddenValue :
37677                 this.value !== undefined ? this.value : '';
37678
37679             // prevent input submission
37680             this.el.dom.removeAttribute('name');
37681              
37682              
37683         }
37684         if(Roo.isGecko){
37685             this.el.dom.setAttribute('autocomplete', 'off');
37686         }
37687
37688         var cls = 'x-combo-list';
37689
37690         this.list = new Roo.Layer({
37691             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37692         });
37693
37694         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37695         this.list.setWidth(lw);
37696         this.list.swallowEvent('mousewheel');
37697         this.assetHeight = 0;
37698
37699         if(this.title){
37700             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37701             this.assetHeight += this.header.getHeight();
37702         }
37703
37704         this.innerList = this.list.createChild({cls:cls+'-inner'});
37705         this.innerList.on('mouseover', this.onViewOver, this);
37706         this.innerList.on('mousemove', this.onViewMove, this);
37707         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37708         
37709         if(this.allowBlank && !this.pageSize && !this.disableClear){
37710             this.footer = this.list.createChild({cls:cls+'-ft'});
37711             this.pageTb = new Roo.Toolbar(this.footer);
37712            
37713         }
37714         if(this.pageSize){
37715             this.footer = this.list.createChild({cls:cls+'-ft'});
37716             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37717                     {pageSize: this.pageSize});
37718             
37719         }
37720         
37721         if (this.pageTb && this.allowBlank && !this.disableClear) {
37722             var _this = this;
37723             this.pageTb.add(new Roo.Toolbar.Fill(), {
37724                 cls: 'x-btn-icon x-btn-clear',
37725                 text: '&#160;',
37726                 handler: function()
37727                 {
37728                     _this.collapse();
37729                     _this.clearValue();
37730                     _this.onSelect(false, -1);
37731                 }
37732             });
37733         }
37734         if (this.footer) {
37735             this.assetHeight += this.footer.getHeight();
37736         }
37737         
37738
37739         if(!this.tpl){
37740             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37741         }
37742
37743         this.view = new Roo.View(this.innerList, this.tpl, {
37744             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37745         });
37746
37747         this.view.on('click', this.onViewClick, this);
37748
37749         this.store.on('beforeload', this.onBeforeLoad, this);
37750         this.store.on('load', this.onLoad, this);
37751         this.store.on('loadexception', this.onLoadException, this);
37752
37753         if(this.resizable){
37754             this.resizer = new Roo.Resizable(this.list,  {
37755                pinned:true, handles:'se'
37756             });
37757             this.resizer.on('resize', function(r, w, h){
37758                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37759                 this.listWidth = w;
37760                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37761                 this.restrictHeight();
37762             }, this);
37763             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37764         }
37765         if(!this.editable){
37766             this.editable = true;
37767             this.setEditable(false);
37768         }  
37769         
37770         
37771         if (typeof(this.events.add.listeners) != 'undefined') {
37772             
37773             this.addicon = this.wrap.createChild(
37774                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37775        
37776             this.addicon.on('click', function(e) {
37777                 this.fireEvent('add', this);
37778             }, this);
37779         }
37780         if (typeof(this.events.edit.listeners) != 'undefined') {
37781             
37782             this.editicon = this.wrap.createChild(
37783                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37784             if (this.addicon) {
37785                 this.editicon.setStyle('margin-left', '40px');
37786             }
37787             this.editicon.on('click', function(e) {
37788                 
37789                 // we fire even  if inothing is selected..
37790                 this.fireEvent('edit', this, this.lastData );
37791                 
37792             }, this);
37793         }
37794         
37795         
37796         
37797     },
37798
37799     // private
37800     initEvents : function(){
37801         Roo.form.ComboBox.superclass.initEvents.call(this);
37802
37803         this.keyNav = new Roo.KeyNav(this.el, {
37804             "up" : function(e){
37805                 this.inKeyMode = true;
37806                 this.selectPrev();
37807             },
37808
37809             "down" : function(e){
37810                 if(!this.isExpanded()){
37811                     this.onTriggerClick();
37812                 }else{
37813                     this.inKeyMode = true;
37814                     this.selectNext();
37815                 }
37816             },
37817
37818             "enter" : function(e){
37819                 this.onViewClick();
37820                 //return true;
37821             },
37822
37823             "esc" : function(e){
37824                 this.collapse();
37825             },
37826
37827             "tab" : function(e){
37828                 this.onViewClick(false);
37829                 this.fireEvent("specialkey", this, e);
37830                 return true;
37831             },
37832
37833             scope : this,
37834
37835             doRelay : function(foo, bar, hname){
37836                 if(hname == 'down' || this.scope.isExpanded()){
37837                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37838                 }
37839                 return true;
37840             },
37841
37842             forceKeyDown: true
37843         });
37844         this.queryDelay = Math.max(this.queryDelay || 10,
37845                 this.mode == 'local' ? 10 : 250);
37846         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37847         if(this.typeAhead){
37848             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37849         }
37850         if(this.editable !== false){
37851             this.el.on("keyup", this.onKeyUp, this);
37852         }
37853         if(this.forceSelection){
37854             this.on('blur', this.doForce, this);
37855         }
37856     },
37857
37858     onDestroy : function(){
37859         if(this.view){
37860             this.view.setStore(null);
37861             this.view.el.removeAllListeners();
37862             this.view.el.remove();
37863             this.view.purgeListeners();
37864         }
37865         if(this.list){
37866             this.list.destroy();
37867         }
37868         if(this.store){
37869             this.store.un('beforeload', this.onBeforeLoad, this);
37870             this.store.un('load', this.onLoad, this);
37871             this.store.un('loadexception', this.onLoadException, this);
37872         }
37873         Roo.form.ComboBox.superclass.onDestroy.call(this);
37874     },
37875
37876     // private
37877     fireKey : function(e){
37878         if(e.isNavKeyPress() && !this.list.isVisible()){
37879             this.fireEvent("specialkey", this, e);
37880         }
37881     },
37882
37883     // private
37884     onResize: function(w, h){
37885         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37886         
37887         if(typeof w != 'number'){
37888             // we do not handle it!?!?
37889             return;
37890         }
37891         var tw = this.trigger.getWidth();
37892         tw += this.addicon ? this.addicon.getWidth() : 0;
37893         tw += this.editicon ? this.editicon.getWidth() : 0;
37894         var x = w - tw;
37895         this.el.setWidth( this.adjustWidth('input', x));
37896             
37897         this.trigger.setStyle('left', x+'px');
37898         
37899         if(this.list && this.listWidth === undefined){
37900             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37901             this.list.setWidth(lw);
37902             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37903         }
37904         
37905     
37906         
37907     },
37908
37909     /**
37910      * Allow or prevent the user from directly editing the field text.  If false is passed,
37911      * the user will only be able to select from the items defined in the dropdown list.  This method
37912      * is the runtime equivalent of setting the 'editable' config option at config time.
37913      * @param {Boolean} value True to allow the user to directly edit the field text
37914      */
37915     setEditable : function(value){
37916         if(value == this.editable){
37917             return;
37918         }
37919         this.editable = value;
37920         if(!value){
37921             this.el.dom.setAttribute('readOnly', true);
37922             this.el.on('mousedown', this.onTriggerClick,  this);
37923             this.el.addClass('x-combo-noedit');
37924         }else{
37925             this.el.dom.setAttribute('readOnly', false);
37926             this.el.un('mousedown', this.onTriggerClick,  this);
37927             this.el.removeClass('x-combo-noedit');
37928         }
37929     },
37930
37931     // private
37932     onBeforeLoad : function(){
37933         if(!this.hasFocus){
37934             return;
37935         }
37936         this.innerList.update(this.loadingText ?
37937                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37938         this.restrictHeight();
37939         this.selectedIndex = -1;
37940     },
37941
37942     // private
37943     onLoad : function(){
37944         if(!this.hasFocus){
37945             return;
37946         }
37947         if(this.store.getCount() > 0){
37948             this.expand();
37949             this.restrictHeight();
37950             if(this.lastQuery == this.allQuery){
37951                 if(this.editable){
37952                     this.el.dom.select();
37953                 }
37954                 if(!this.selectByValue(this.value, true)){
37955                     this.select(0, true);
37956                 }
37957             }else{
37958                 this.selectNext();
37959                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37960                     this.taTask.delay(this.typeAheadDelay);
37961                 }
37962             }
37963         }else{
37964             this.onEmptyResults();
37965         }
37966         //this.el.focus();
37967     },
37968     // private
37969     onLoadException : function()
37970     {
37971         this.collapse();
37972         Roo.log(this.store.reader.jsonData);
37973         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37974             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37975         }
37976         
37977         
37978     },
37979     // private
37980     onTypeAhead : function(){
37981         if(this.store.getCount() > 0){
37982             var r = this.store.getAt(0);
37983             var newValue = r.data[this.displayField];
37984             var len = newValue.length;
37985             var selStart = this.getRawValue().length;
37986             if(selStart != len){
37987                 this.setRawValue(newValue);
37988                 this.selectText(selStart, newValue.length);
37989             }
37990         }
37991     },
37992
37993     // private
37994     onSelect : function(record, index){
37995         if(this.fireEvent('beforeselect', this, record, index) !== false){
37996             this.setFromData(index > -1 ? record.data : false);
37997             this.collapse();
37998             this.fireEvent('select', this, record, index);
37999         }
38000     },
38001
38002     /**
38003      * Returns the currently selected field value or empty string if no value is set.
38004      * @return {String} value The selected value
38005      */
38006     getValue : function(){
38007         if(this.valueField){
38008             return typeof this.value != 'undefined' ? this.value : '';
38009         }else{
38010             return Roo.form.ComboBox.superclass.getValue.call(this);
38011         }
38012     },
38013
38014     /**
38015      * Clears any text/value currently set in the field
38016      */
38017     clearValue : function(){
38018         if(this.hiddenField){
38019             this.hiddenField.value = '';
38020         }
38021         this.value = '';
38022         this.setRawValue('');
38023         this.lastSelectionText = '';
38024         this.applyEmptyText();
38025     },
38026
38027     /**
38028      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38029      * will be displayed in the field.  If the value does not match the data value of an existing item,
38030      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38031      * Otherwise the field will be blank (although the value will still be set).
38032      * @param {String} value The value to match
38033      */
38034     setValue : function(v){
38035         var text = v;
38036         if(this.valueField){
38037             var r = this.findRecord(this.valueField, v);
38038             if(r){
38039                 text = r.data[this.displayField];
38040             }else if(this.valueNotFoundText !== undefined){
38041                 text = this.valueNotFoundText;
38042             }
38043         }
38044         this.lastSelectionText = text;
38045         if(this.hiddenField){
38046             this.hiddenField.value = v;
38047         }
38048         Roo.form.ComboBox.superclass.setValue.call(this, text);
38049         this.value = v;
38050     },
38051     /**
38052      * @property {Object} the last set data for the element
38053      */
38054     
38055     lastData : false,
38056     /**
38057      * Sets the value of the field based on a object which is related to the record format for the store.
38058      * @param {Object} value the value to set as. or false on reset?
38059      */
38060     setFromData : function(o){
38061         var dv = ''; // display value
38062         var vv = ''; // value value..
38063         this.lastData = o;
38064         if (this.displayField) {
38065             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
38066         } else {
38067             // this is an error condition!!!
38068             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
38069         }
38070         
38071         if(this.valueField){
38072             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
38073         }
38074         if(this.hiddenField){
38075             this.hiddenField.value = vv;
38076             
38077             this.lastSelectionText = dv;
38078             Roo.form.ComboBox.superclass.setValue.call(this, dv);
38079             this.value = vv;
38080             return;
38081         }
38082         // no hidden field.. - we store the value in 'value', but still display
38083         // display field!!!!
38084         this.lastSelectionText = dv;
38085         Roo.form.ComboBox.superclass.setValue.call(this, dv);
38086         this.value = vv;
38087         
38088         
38089     },
38090     // private
38091     reset : function(){
38092         // overridden so that last data is reset..
38093         this.setValue(this.originalValue);
38094         this.clearInvalid();
38095         this.lastData = false;
38096     },
38097     // private
38098     findRecord : function(prop, value){
38099         var record;
38100         if(this.store.getCount() > 0){
38101             this.store.each(function(r){
38102                 if(r.data[prop] == value){
38103                     record = r;
38104                     return false;
38105                 }
38106                 return true;
38107             });
38108         }
38109         return record;
38110     },
38111     
38112     getName: function()
38113     {
38114         // returns hidden if it's set..
38115         if (!this.rendered) {return ''};
38116         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38117         
38118     },
38119     // private
38120     onViewMove : function(e, t){
38121         this.inKeyMode = false;
38122     },
38123
38124     // private
38125     onViewOver : function(e, t){
38126         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38127             return;
38128         }
38129         var item = this.view.findItemFromChild(t);
38130         if(item){
38131             var index = this.view.indexOf(item);
38132             this.select(index, false);
38133         }
38134     },
38135
38136     // private
38137     onViewClick : function(doFocus)
38138     {
38139         var index = this.view.getSelectedIndexes()[0];
38140         var r = this.store.getAt(index);
38141         if(r){
38142             this.onSelect(r, index);
38143         }
38144         if(doFocus !== false && !this.blockFocus){
38145             this.el.focus();
38146         }
38147     },
38148
38149     // private
38150     restrictHeight : function(){
38151         this.innerList.dom.style.height = '';
38152         var inner = this.innerList.dom;
38153         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38154         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38155         this.list.beginUpdate();
38156         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38157         this.list.alignTo(this.el, this.listAlign);
38158         this.list.endUpdate();
38159     },
38160
38161     // private
38162     onEmptyResults : function(){
38163         this.collapse();
38164     },
38165
38166     /**
38167      * Returns true if the dropdown list is expanded, else false.
38168      */
38169     isExpanded : function(){
38170         return this.list.isVisible();
38171     },
38172
38173     /**
38174      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38175      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38176      * @param {String} value The data value of the item to select
38177      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38178      * selected item if it is not currently in view (defaults to true)
38179      * @return {Boolean} True if the value matched an item in the list, else false
38180      */
38181     selectByValue : function(v, scrollIntoView){
38182         if(v !== undefined && v !== null){
38183             var r = this.findRecord(this.valueField || this.displayField, v);
38184             if(r){
38185                 this.select(this.store.indexOf(r), scrollIntoView);
38186                 return true;
38187             }
38188         }
38189         return false;
38190     },
38191
38192     /**
38193      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38194      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38195      * @param {Number} index The zero-based index of the list item to select
38196      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38197      * selected item if it is not currently in view (defaults to true)
38198      */
38199     select : function(index, scrollIntoView){
38200         this.selectedIndex = index;
38201         this.view.select(index);
38202         if(scrollIntoView !== false){
38203             var el = this.view.getNode(index);
38204             if(el){
38205                 this.innerList.scrollChildIntoView(el, false);
38206             }
38207         }
38208     },
38209
38210     // private
38211     selectNext : function(){
38212         var ct = this.store.getCount();
38213         if(ct > 0){
38214             if(this.selectedIndex == -1){
38215                 this.select(0);
38216             }else if(this.selectedIndex < ct-1){
38217                 this.select(this.selectedIndex+1);
38218             }
38219         }
38220     },
38221
38222     // private
38223     selectPrev : function(){
38224         var ct = this.store.getCount();
38225         if(ct > 0){
38226             if(this.selectedIndex == -1){
38227                 this.select(0);
38228             }else if(this.selectedIndex != 0){
38229                 this.select(this.selectedIndex-1);
38230             }
38231         }
38232     },
38233
38234     // private
38235     onKeyUp : function(e){
38236         if(this.editable !== false && !e.isSpecialKey()){
38237             this.lastKey = e.getKey();
38238             this.dqTask.delay(this.queryDelay);
38239         }
38240     },
38241
38242     // private
38243     validateBlur : function(){
38244         return !this.list || !this.list.isVisible();   
38245     },
38246
38247     // private
38248     initQuery : function(){
38249         this.doQuery(this.getRawValue());
38250     },
38251
38252     // private
38253     doForce : function(){
38254         if(this.el.dom.value.length > 0){
38255             this.el.dom.value =
38256                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38257             this.applyEmptyText();
38258         }
38259     },
38260
38261     /**
38262      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38263      * query allowing the query action to be canceled if needed.
38264      * @param {String} query The SQL query to execute
38265      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38266      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38267      * saved in the current store (defaults to false)
38268      */
38269     doQuery : function(q, forceAll){
38270         if(q === undefined || q === null){
38271             q = '';
38272         }
38273         var qe = {
38274             query: q,
38275             forceAll: forceAll,
38276             combo: this,
38277             cancel:false
38278         };
38279         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38280             return false;
38281         }
38282         q = qe.query;
38283         forceAll = qe.forceAll;
38284         if(forceAll === true || (q.length >= this.minChars)){
38285             if(this.lastQuery != q || this.alwaysQuery){
38286                 this.lastQuery = q;
38287                 if(this.mode == 'local'){
38288                     this.selectedIndex = -1;
38289                     if(forceAll){
38290                         this.store.clearFilter();
38291                     }else{
38292                         this.store.filter(this.displayField, q);
38293                     }
38294                     this.onLoad();
38295                 }else{
38296                     this.store.baseParams[this.queryParam] = q;
38297                     this.store.load({
38298                         params: this.getParams(q)
38299                     });
38300                     this.expand();
38301                 }
38302             }else{
38303                 this.selectedIndex = -1;
38304                 this.onLoad();   
38305             }
38306         }
38307     },
38308
38309     // private
38310     getParams : function(q){
38311         var p = {};
38312         //p[this.queryParam] = q;
38313         if(this.pageSize){
38314             p.start = 0;
38315             p.limit = this.pageSize;
38316         }
38317         return p;
38318     },
38319
38320     /**
38321      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38322      */
38323     collapse : function(){
38324         if(!this.isExpanded()){
38325             return;
38326         }
38327         this.list.hide();
38328         Roo.get(document).un('mousedown', this.collapseIf, this);
38329         Roo.get(document).un('mousewheel', this.collapseIf, this);
38330         if (!this.editable) {
38331             Roo.get(document).un('keydown', this.listKeyPress, this);
38332         }
38333         this.fireEvent('collapse', this);
38334     },
38335
38336     // private
38337     collapseIf : function(e){
38338         if(!e.within(this.wrap) && !e.within(this.list)){
38339             this.collapse();
38340         }
38341     },
38342
38343     /**
38344      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38345      */
38346     expand : function(){
38347         if(this.isExpanded() || !this.hasFocus){
38348             return;
38349         }
38350         this.list.alignTo(this.el, this.listAlign);
38351         this.list.show();
38352         Roo.get(document).on('mousedown', this.collapseIf, this);
38353         Roo.get(document).on('mousewheel', this.collapseIf, this);
38354         if (!this.editable) {
38355             Roo.get(document).on('keydown', this.listKeyPress, this);
38356         }
38357         
38358         this.fireEvent('expand', this);
38359     },
38360
38361     // private
38362     // Implements the default empty TriggerField.onTriggerClick function
38363     onTriggerClick : function(){
38364         if(this.disabled){
38365             return;
38366         }
38367         if(this.isExpanded()){
38368             this.collapse();
38369             if (!this.blockFocus) {
38370                 this.el.focus();
38371             }
38372             
38373         }else {
38374             this.hasFocus = true;
38375             if(this.triggerAction == 'all') {
38376                 this.doQuery(this.allQuery, true);
38377             } else {
38378                 this.doQuery(this.getRawValue());
38379             }
38380             if (!this.blockFocus) {
38381                 this.el.focus();
38382             }
38383         }
38384     },
38385     listKeyPress : function(e)
38386     {
38387         //Roo.log('listkeypress');
38388         // scroll to first matching element based on key pres..
38389         if (e.isSpecialKey()) {
38390             return false;
38391         }
38392         var k = String.fromCharCode(e.getKey()).toUpperCase();
38393         //Roo.log(k);
38394         var match  = false;
38395         var csel = this.view.getSelectedNodes();
38396         var cselitem = false;
38397         if (csel.length) {
38398             var ix = this.view.indexOf(csel[0]);
38399             cselitem  = this.store.getAt(ix);
38400             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38401                 cselitem = false;
38402             }
38403             
38404         }
38405         
38406         this.store.each(function(v) { 
38407             if (cselitem) {
38408                 // start at existing selection.
38409                 if (cselitem.id == v.id) {
38410                     cselitem = false;
38411                 }
38412                 return;
38413             }
38414                 
38415             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38416                 match = this.store.indexOf(v);
38417                 return false;
38418             }
38419         }, this);
38420         
38421         if (match === false) {
38422             return true; // no more action?
38423         }
38424         // scroll to?
38425         this.view.select(match);
38426         var sn = Roo.get(this.view.getSelectedNodes()[0])
38427         sn.scrollIntoView(sn.dom.parentNode, false);
38428     }
38429
38430     /** 
38431     * @cfg {Boolean} grow 
38432     * @hide 
38433     */
38434     /** 
38435     * @cfg {Number} growMin 
38436     * @hide 
38437     */
38438     /** 
38439     * @cfg {Number} growMax 
38440     * @hide 
38441     */
38442     /**
38443      * @hide
38444      * @method autoSize
38445      */
38446 });/*
38447  * Based on:
38448  * Ext JS Library 1.1.1
38449  * Copyright(c) 2006-2007, Ext JS, LLC.
38450  *
38451  * Originally Released Under LGPL - original licence link has changed is not relivant.
38452  *
38453  * Fork - LGPL
38454  * <script type="text/javascript">
38455  */
38456 /**
38457  * @class Roo.form.Checkbox
38458  * @extends Roo.form.Field
38459  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38460  * @constructor
38461  * Creates a new Checkbox
38462  * @param {Object} config Configuration options
38463  */
38464 Roo.form.Checkbox = function(config){
38465     Roo.form.Checkbox.superclass.constructor.call(this, config);
38466     this.addEvents({
38467         /**
38468          * @event check
38469          * Fires when the checkbox is checked or unchecked.
38470              * @param {Roo.form.Checkbox} this This checkbox
38471              * @param {Boolean} checked The new checked value
38472              */
38473         check : true
38474     });
38475 };
38476
38477 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38478     /**
38479      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38480      */
38481     focusClass : undefined,
38482     /**
38483      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38484      */
38485     fieldClass: "x-form-field",
38486     /**
38487      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38488      */
38489     checked: false,
38490     /**
38491      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38492      * {tag: "input", type: "checkbox", autocomplete: "off"})
38493      */
38494     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38495     /**
38496      * @cfg {String} boxLabel The text that appears beside the checkbox
38497      */
38498     boxLabel : "",
38499     /**
38500      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38501      */  
38502     inputValue : '1',
38503     /**
38504      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38505      */
38506      valueOff: '0', // value when not checked..
38507
38508     actionMode : 'viewEl', 
38509     //
38510     // private
38511     itemCls : 'x-menu-check-item x-form-item',
38512     groupClass : 'x-menu-group-item',
38513     inputType : 'hidden',
38514     
38515     
38516     inSetChecked: false, // check that we are not calling self...
38517     
38518     inputElement: false, // real input element?
38519     basedOn: false, // ????
38520     
38521     isFormField: true, // not sure where this is needed!!!!
38522
38523     onResize : function(){
38524         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38525         if(!this.boxLabel){
38526             this.el.alignTo(this.wrap, 'c-c');
38527         }
38528     },
38529
38530     initEvents : function(){
38531         Roo.form.Checkbox.superclass.initEvents.call(this);
38532         this.el.on("click", this.onClick,  this);
38533         this.el.on("change", this.onClick,  this);
38534     },
38535
38536
38537     getResizeEl : function(){
38538         return this.wrap;
38539     },
38540
38541     getPositionEl : function(){
38542         return this.wrap;
38543     },
38544
38545     // private
38546     onRender : function(ct, position){
38547         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38548         /*
38549         if(this.inputValue !== undefined){
38550             this.el.dom.value = this.inputValue;
38551         }
38552         */
38553         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38554         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38555         var viewEl = this.wrap.createChild({ 
38556             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38557         this.viewEl = viewEl;   
38558         this.wrap.on('click', this.onClick,  this); 
38559         
38560         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38561         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38562         
38563         
38564         
38565         if(this.boxLabel){
38566             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38567         //    viewEl.on('click', this.onClick,  this); 
38568         }
38569         //if(this.checked){
38570             this.setChecked(this.checked);
38571         //}else{
38572             //this.checked = this.el.dom;
38573         //}
38574
38575     },
38576
38577     // private
38578     initValue : Roo.emptyFn,
38579
38580     /**
38581      * Returns the checked state of the checkbox.
38582      * @return {Boolean} True if checked, else false
38583      */
38584     getValue : function(){
38585         if(this.el){
38586             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38587         }
38588         return this.valueOff;
38589         
38590     },
38591
38592         // private
38593     onClick : function(){ 
38594         this.setChecked(!this.checked);
38595
38596         //if(this.el.dom.checked != this.checked){
38597         //    this.setValue(this.el.dom.checked);
38598        // }
38599     },
38600
38601     /**
38602      * Sets the checked state of the checkbox.
38603      * On is always based on a string comparison between inputValue and the param.
38604      * @param {Boolean/String} value - the value to set 
38605      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38606      */
38607     setValue : function(v,suppressEvent){
38608         
38609         
38610         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38611         //if(this.el && this.el.dom){
38612         //    this.el.dom.checked = this.checked;
38613         //    this.el.dom.defaultChecked = this.checked;
38614         //}
38615         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38616         //this.fireEvent("check", this, this.checked);
38617     },
38618     // private..
38619     setChecked : function(state,suppressEvent)
38620     {
38621         if (this.inSetChecked) {
38622             this.checked = state;
38623             return;
38624         }
38625         
38626     
38627         if(this.wrap){
38628             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38629         }
38630         this.checked = state;
38631         if(suppressEvent !== true){
38632             this.fireEvent('check', this, state);
38633         }
38634         this.inSetChecked = true;
38635         this.el.dom.value = state ? this.inputValue : this.valueOff;
38636         this.inSetChecked = false;
38637         
38638     },
38639     // handle setting of hidden value by some other method!!?!?
38640     setFromHidden: function()
38641     {
38642         if(!this.el){
38643             return;
38644         }
38645         //console.log("SET FROM HIDDEN");
38646         //alert('setFrom hidden');
38647         this.setValue(this.el.dom.value);
38648     },
38649     
38650     onDestroy : function()
38651     {
38652         if(this.viewEl){
38653             Roo.get(this.viewEl).remove();
38654         }
38655          
38656         Roo.form.Checkbox.superclass.onDestroy.call(this);
38657     }
38658
38659 });/*
38660  * Based on:
38661  * Ext JS Library 1.1.1
38662  * Copyright(c) 2006-2007, Ext JS, LLC.
38663  *
38664  * Originally Released Under LGPL - original licence link has changed is not relivant.
38665  *
38666  * Fork - LGPL
38667  * <script type="text/javascript">
38668  */
38669  
38670 /**
38671  * @class Roo.form.Radio
38672  * @extends Roo.form.Checkbox
38673  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38674  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38675  * @constructor
38676  * Creates a new Radio
38677  * @param {Object} config Configuration options
38678  */
38679 Roo.form.Radio = function(){
38680     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38681 };
38682 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38683     inputType: 'radio',
38684
38685     /**
38686      * If this radio is part of a group, it will return the selected value
38687      * @return {String}
38688      */
38689     getGroupValue : function(){
38690         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38691     }
38692 });//<script type="text/javascript">
38693
38694 /*
38695  * Ext JS Library 1.1.1
38696  * Copyright(c) 2006-2007, Ext JS, LLC.
38697  * licensing@extjs.com
38698  * 
38699  * http://www.extjs.com/license
38700  */
38701  
38702  /*
38703   * 
38704   * Known bugs:
38705   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38706   * - IE ? - no idea how much works there.
38707   * 
38708   * 
38709   * 
38710   */
38711  
38712
38713 /**
38714  * @class Ext.form.HtmlEditor
38715  * @extends Ext.form.Field
38716  * Provides a lightweight HTML Editor component.
38717  *
38718  * This has been tested on Fireforx / Chrome.. IE may not be so great..
38719  * 
38720  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38721  * supported by this editor.</b><br/><br/>
38722  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38723  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38724  */
38725 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38726       /**
38727      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38728      */
38729     toolbars : false,
38730     /**
38731      * @cfg {String} createLinkText The default text for the create link prompt
38732      */
38733     createLinkText : 'Please enter the URL for the link:',
38734     /**
38735      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38736      */
38737     defaultLinkValue : 'http:/'+'/',
38738    
38739      /**
38740      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38741      *                        Roo.resizable.
38742      */
38743     resizable : false,
38744      /**
38745      * @cfg {Number} height (in pixels)
38746      */   
38747     height: 300,
38748    /**
38749      * @cfg {Number} width (in pixels)
38750      */   
38751     width: 500,
38752     
38753     /**
38754      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38755      * 
38756      */
38757     stylesheets: false,
38758     
38759     // id of frame..
38760     frameId: false,
38761     
38762     // private properties
38763     validationEvent : false,
38764     deferHeight: true,
38765     initialized : false,
38766     activated : false,
38767     sourceEditMode : false,
38768     onFocus : Roo.emptyFn,
38769     iframePad:3,
38770     hideMode:'offsets',
38771     
38772     defaultAutoCreate : { // modified by initCompnoent..
38773         tag: "textarea",
38774         style:"width:500px;height:300px;",
38775         autocomplete: "off"
38776     },
38777
38778     // private
38779     initComponent : function(){
38780         this.addEvents({
38781             /**
38782              * @event initialize
38783              * Fires when the editor is fully initialized (including the iframe)
38784              * @param {HtmlEditor} this
38785              */
38786             initialize: true,
38787             /**
38788              * @event activate
38789              * Fires when the editor is first receives the focus. Any insertion must wait
38790              * until after this event.
38791              * @param {HtmlEditor} this
38792              */
38793             activate: true,
38794              /**
38795              * @event beforesync
38796              * Fires before the textarea is updated with content from the editor iframe. Return false
38797              * to cancel the sync.
38798              * @param {HtmlEditor} this
38799              * @param {String} html
38800              */
38801             beforesync: true,
38802              /**
38803              * @event beforepush
38804              * Fires before the iframe editor is updated with content from the textarea. Return false
38805              * to cancel the push.
38806              * @param {HtmlEditor} this
38807              * @param {String} html
38808              */
38809             beforepush: true,
38810              /**
38811              * @event sync
38812              * Fires when the textarea is updated with content from the editor iframe.
38813              * @param {HtmlEditor} this
38814              * @param {String} html
38815              */
38816             sync: true,
38817              /**
38818              * @event push
38819              * Fires when the iframe editor is updated with content from the textarea.
38820              * @param {HtmlEditor} this
38821              * @param {String} html
38822              */
38823             push: true,
38824              /**
38825              * @event editmodechange
38826              * Fires when the editor switches edit modes
38827              * @param {HtmlEditor} this
38828              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38829              */
38830             editmodechange: true,
38831             /**
38832              * @event editorevent
38833              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38834              * @param {HtmlEditor} this
38835              */
38836             editorevent: true
38837         });
38838         this.defaultAutoCreate =  {
38839             tag: "textarea",
38840             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38841             autocomplete: "off"
38842         };
38843     },
38844
38845     /**
38846      * Protected method that will not generally be called directly. It
38847      * is called when the editor creates its toolbar. Override this method if you need to
38848      * add custom toolbar buttons.
38849      * @param {HtmlEditor} editor
38850      */
38851     createToolbar : function(editor){
38852         if (!editor.toolbars || !editor.toolbars.length) {
38853             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38854         }
38855         
38856         for (var i =0 ; i < editor.toolbars.length;i++) {
38857             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38858             editor.toolbars[i].init(editor);
38859         }
38860          
38861         
38862     },
38863
38864     /**
38865      * Protected method that will not generally be called directly. It
38866      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38867      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38868      */
38869     getDocMarkup : function(){
38870         // body styles..
38871         var st = '';
38872         if (this.stylesheets === false) {
38873             
38874             Roo.get(document.head).select('style').each(function(node) {
38875                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38876             });
38877             
38878             Roo.get(document.head).select('link').each(function(node) { 
38879                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38880             });
38881             
38882         } else if (!this.stylesheets.length) {
38883                 // simple..
38884                 st = '<style type="text/css">' +
38885                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38886                    '</style>';
38887         } else {
38888             Roo.each(this.stylesheets, function(s) {
38889                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38890             });
38891             
38892         }
38893         
38894         st +=  '<style type="text/css">' +
38895             'IMG { cursor: pointer } ' +
38896         '</style>';
38897
38898         
38899         return '<html><head>' + st  +
38900             //<style type="text/css">' +
38901             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38902             //'</style>' +
38903             ' </head><body></body></html>';
38904     },
38905
38906     // private
38907     onRender : function(ct, position)
38908     {
38909         var _t = this;
38910         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38911         this.el.dom.style.border = '0 none';
38912         this.el.dom.setAttribute('tabIndex', -1);
38913         this.el.addClass('x-hidden');
38914         if(Roo.isIE){ // fix IE 1px bogus margin
38915             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38916         }
38917         this.wrap = this.el.wrap({
38918             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38919         });
38920         
38921         if (this.resizable) {
38922             this.resizeEl = new Roo.Resizable(this.wrap, {
38923                 pinned : true,
38924                 wrap: true,
38925                 dynamic : true,
38926                 minHeight : this.height,
38927                 height: this.height,
38928                 handles : this.resizable,
38929                 width: this.width,
38930                 listeners : {
38931                     resize : function(r, w, h) {
38932                         _t.onResize(w,h); // -something
38933                     }
38934                 }
38935             });
38936             
38937         }
38938
38939         this.frameId = Roo.id();
38940         
38941         this.createToolbar(this);
38942         
38943       
38944         
38945         var iframe = this.wrap.createChild({
38946             tag: 'iframe',
38947             id: this.frameId,
38948             name: this.frameId,
38949             frameBorder : 'no',
38950             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38951         }, this.el
38952         );
38953         
38954        // console.log(iframe);
38955         //this.wrap.dom.appendChild(iframe);
38956
38957         this.iframe = iframe.dom;
38958
38959          this.assignDocWin();
38960         
38961         this.doc.designMode = 'on';
38962        
38963         this.doc.open();
38964         this.doc.write(this.getDocMarkup());
38965         this.doc.close();
38966
38967         
38968         var task = { // must defer to wait for browser to be ready
38969             run : function(){
38970                 //console.log("run task?" + this.doc.readyState);
38971                 this.assignDocWin();
38972                 if(this.doc.body || this.doc.readyState == 'complete'){
38973                     try {
38974                         this.doc.designMode="on";
38975                     } catch (e) {
38976                         return;
38977                     }
38978                     Roo.TaskMgr.stop(task);
38979                     this.initEditor.defer(10, this);
38980                 }
38981             },
38982             interval : 10,
38983             duration:10000,
38984             scope: this
38985         };
38986         Roo.TaskMgr.start(task);
38987
38988         if(!this.width){
38989             this.setSize(this.wrap.getSize());
38990         }
38991         if (this.resizeEl) {
38992             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38993             // should trigger onReize..
38994         }
38995     },
38996
38997     // private
38998     onResize : function(w, h)
38999     {
39000         //Roo.log('resize: ' +w + ',' + h );
39001         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
39002         if(this.el && this.iframe){
39003             if(typeof w == 'number'){
39004                 var aw = w - this.wrap.getFrameWidth('lr');
39005                 this.el.setWidth(this.adjustWidth('textarea', aw));
39006                 this.iframe.style.width = aw + 'px';
39007             }
39008             if(typeof h == 'number'){
39009                 var tbh = 0;
39010                 for (var i =0; i < this.toolbars.length;i++) {
39011                     // fixme - ask toolbars for heights?
39012                     tbh += this.toolbars[i].tb.el.getHeight();
39013                     if (this.toolbars[i].footer) {
39014                         tbh += this.toolbars[i].footer.el.getHeight();
39015                     }
39016                 }
39017                 
39018                 
39019                 
39020                 
39021                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
39022                 ah -= 5; // knock a few pixes off for look..
39023                 this.el.setHeight(this.adjustWidth('textarea', ah));
39024                 this.iframe.style.height = ah + 'px';
39025                 if(this.doc){
39026                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
39027                 }
39028             }
39029         }
39030     },
39031
39032     /**
39033      * Toggles the editor between standard and source edit mode.
39034      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
39035      */
39036     toggleSourceEdit : function(sourceEditMode){
39037         
39038         this.sourceEditMode = sourceEditMode === true;
39039         
39040         if(this.sourceEditMode){
39041           
39042             this.syncValue();
39043             this.iframe.className = 'x-hidden';
39044             this.el.removeClass('x-hidden');
39045             this.el.dom.removeAttribute('tabIndex');
39046             this.el.focus();
39047         }else{
39048              
39049             this.pushValue();
39050             this.iframe.className = '';
39051             this.el.addClass('x-hidden');
39052             this.el.dom.setAttribute('tabIndex', -1);
39053             this.deferFocus();
39054         }
39055         this.setSize(this.wrap.getSize());
39056         this.fireEvent('editmodechange', this, this.sourceEditMode);
39057     },
39058
39059     // private used internally
39060     createLink : function(){
39061         var url = prompt(this.createLinkText, this.defaultLinkValue);
39062         if(url && url != 'http:/'+'/'){
39063             this.relayCmd('createlink', url);
39064         }
39065     },
39066
39067     // private (for BoxComponent)
39068     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39069
39070     // private (for BoxComponent)
39071     getResizeEl : function(){
39072         return this.wrap;
39073     },
39074
39075     // private (for BoxComponent)
39076     getPositionEl : function(){
39077         return this.wrap;
39078     },
39079
39080     // private
39081     initEvents : function(){
39082         this.originalValue = this.getValue();
39083     },
39084
39085     /**
39086      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39087      * @method
39088      */
39089     markInvalid : Roo.emptyFn,
39090     /**
39091      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39092      * @method
39093      */
39094     clearInvalid : Roo.emptyFn,
39095
39096     setValue : function(v){
39097         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
39098         this.pushValue();
39099     },
39100
39101     /**
39102      * Protected method that will not generally be called directly. If you need/want
39103      * custom HTML cleanup, this is the method you should override.
39104      * @param {String} html The HTML to be cleaned
39105      * return {String} The cleaned HTML
39106      */
39107     cleanHtml : function(html){
39108         html = String(html);
39109         if(html.length > 5){
39110             if(Roo.isSafari){ // strip safari nonsense
39111                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39112             }
39113         }
39114         if(html == '&nbsp;'){
39115             html = '';
39116         }
39117         return html;
39118     },
39119
39120     /**
39121      * Protected method that will not generally be called directly. Syncs the contents
39122      * of the editor iframe with the textarea.
39123      */
39124     syncValue : function(){
39125         if(this.initialized){
39126             var bd = (this.doc.body || this.doc.documentElement);
39127             //this.cleanUpPaste(); -- this is done else where and causes havoc..
39128             var html = bd.innerHTML;
39129             if(Roo.isSafari){
39130                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39131                 var m = bs.match(/text-align:(.*?);/i);
39132                 if(m && m[1]){
39133                     html = '<div style="'+m[0]+'">' + html + '</div>';
39134                 }
39135             }
39136             html = this.cleanHtml(html);
39137             // fix up the special chars..
39138             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
39139                 return "&#"+b.charCodeAt()+";" 
39140             });
39141             if(this.fireEvent('beforesync', this, html) !== false){
39142                 this.el.dom.value = html;
39143                 this.fireEvent('sync', this, html);
39144             }
39145         }
39146     },
39147
39148     /**
39149      * Protected method that will not generally be called directly. Pushes the value of the textarea
39150      * into the iframe editor.
39151      */
39152     pushValue : function(){
39153         if(this.initialized){
39154             var v = this.el.dom.value;
39155             if(v.length < 1){
39156                 v = '&#160;';
39157             }
39158             
39159             if(this.fireEvent('beforepush', this, v) !== false){
39160                 var d = (this.doc.body || this.doc.documentElement);
39161                 d.innerHTML = v;
39162                 this.cleanUpPaste();
39163                 this.el.dom.value = d.innerHTML;
39164                 this.fireEvent('push', this, v);
39165             }
39166         }
39167     },
39168
39169     // private
39170     deferFocus : function(){
39171         this.focus.defer(10, this);
39172     },
39173
39174     // doc'ed in Field
39175     focus : function(){
39176         if(this.win && !this.sourceEditMode){
39177             this.win.focus();
39178         }else{
39179             this.el.focus();
39180         }
39181     },
39182     
39183     assignDocWin: function()
39184     {
39185         var iframe = this.iframe;
39186         
39187          if(Roo.isIE){
39188             this.doc = iframe.contentWindow.document;
39189             this.win = iframe.contentWindow;
39190         } else {
39191             if (!Roo.get(this.frameId)) {
39192                 return;
39193             }
39194             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39195             this.win = Roo.get(this.frameId).dom.contentWindow;
39196         }
39197     },
39198     
39199     // private
39200     initEditor : function(){
39201         //console.log("INIT EDITOR");
39202         this.assignDocWin();
39203         
39204         
39205         
39206         this.doc.designMode="on";
39207         this.doc.open();
39208         this.doc.write(this.getDocMarkup());
39209         this.doc.close();
39210         
39211         var dbody = (this.doc.body || this.doc.documentElement);
39212         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39213         // this copies styles from the containing element into thsi one..
39214         // not sure why we need all of this..
39215         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39216         ss['background-attachment'] = 'fixed'; // w3c
39217         dbody.bgProperties = 'fixed'; // ie
39218         Roo.DomHelper.applyStyles(dbody, ss);
39219         Roo.EventManager.on(this.doc, {
39220             //'mousedown': this.onEditorEvent,
39221             'mouseup': this.onEditorEvent,
39222             'dblclick': this.onEditorEvent,
39223             'click': this.onEditorEvent,
39224             'keyup': this.onEditorEvent,
39225             buffer:100,
39226             scope: this
39227         });
39228         if(Roo.isGecko){
39229             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39230         }
39231         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39232             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39233         }
39234         this.initialized = true;
39235
39236         this.fireEvent('initialize', this);
39237         this.pushValue();
39238     },
39239
39240     // private
39241     onDestroy : function(){
39242         
39243         
39244         
39245         if(this.rendered){
39246             
39247             for (var i =0; i < this.toolbars.length;i++) {
39248                 // fixme - ask toolbars for heights?
39249                 this.toolbars[i].onDestroy();
39250             }
39251             
39252             this.wrap.dom.innerHTML = '';
39253             this.wrap.remove();
39254         }
39255     },
39256
39257     // private
39258     onFirstFocus : function(){
39259         
39260         this.assignDocWin();
39261         
39262         
39263         this.activated = true;
39264         for (var i =0; i < this.toolbars.length;i++) {
39265             this.toolbars[i].onFirstFocus();
39266         }
39267        
39268         if(Roo.isGecko){ // prevent silly gecko errors
39269             this.win.focus();
39270             var s = this.win.getSelection();
39271             if(!s.focusNode || s.focusNode.nodeType != 3){
39272                 var r = s.getRangeAt(0);
39273                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39274                 r.collapse(true);
39275                 this.deferFocus();
39276             }
39277             try{
39278                 this.execCmd('useCSS', true);
39279                 this.execCmd('styleWithCSS', false);
39280             }catch(e){}
39281         }
39282         this.fireEvent('activate', this);
39283     },
39284
39285     // private
39286     adjustFont: function(btn){
39287         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39288         //if(Roo.isSafari){ // safari
39289         //    adjust *= 2;
39290        // }
39291         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39292         if(Roo.isSafari){ // safari
39293             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39294             v =  (v < 10) ? 10 : v;
39295             v =  (v > 48) ? 48 : v;
39296             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39297             
39298         }
39299         
39300         
39301         v = Math.max(1, v+adjust);
39302         
39303         this.execCmd('FontSize', v  );
39304     },
39305
39306     onEditorEvent : function(e){
39307         this.fireEvent('editorevent', this, e);
39308       //  this.updateToolbar();
39309         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39310     },
39311
39312     insertTag : function(tg)
39313     {
39314         // could be a bit smarter... -> wrap the current selected tRoo..
39315         
39316         this.execCmd("formatblock",   tg);
39317         
39318     },
39319     
39320     insertText : function(txt)
39321     {
39322         
39323         
39324         range = this.createRange();
39325         range.deleteContents();
39326                //alert(Sender.getAttribute('label'));
39327                
39328         range.insertNode(this.doc.createTextNode(txt));
39329     } ,
39330     
39331     // private
39332     relayBtnCmd : function(btn){
39333         this.relayCmd(btn.cmd);
39334     },
39335
39336     /**
39337      * Executes a Midas editor command on the editor document and performs necessary focus and
39338      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39339      * @param {String} cmd The Midas command
39340      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39341      */
39342     relayCmd : function(cmd, value){
39343         this.win.focus();
39344         this.execCmd(cmd, value);
39345         this.fireEvent('editorevent', this);
39346         //this.updateToolbar();
39347         this.deferFocus();
39348     },
39349
39350     /**
39351      * Executes a Midas editor command directly on the editor document.
39352      * For visual commands, you should use {@link #relayCmd} instead.
39353      * <b>This should only be called after the editor is initialized.</b>
39354      * @param {String} cmd The Midas command
39355      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39356      */
39357     execCmd : function(cmd, value){
39358         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39359         this.syncValue();
39360     },
39361  
39362  
39363    
39364     /**
39365      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39366      * to insert tRoo.
39367      * @param {String} text | dom node.. 
39368      */
39369     insertAtCursor : function(text)
39370     {
39371         
39372         
39373         
39374         if(!this.activated){
39375             return;
39376         }
39377         /*
39378         if(Roo.isIE){
39379             this.win.focus();
39380             var r = this.doc.selection.createRange();
39381             if(r){
39382                 r.collapse(true);
39383                 r.pasteHTML(text);
39384                 this.syncValue();
39385                 this.deferFocus();
39386             
39387             }
39388             return;
39389         }
39390         */
39391         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39392             this.win.focus();
39393             
39394             
39395             // from jquery ui (MIT licenced)
39396             var range, node;
39397             var win = this.win;
39398             
39399             if (win.getSelection && win.getSelection().getRangeAt) {
39400                 range = win.getSelection().getRangeAt(0);
39401                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39402                 range.insertNode(node);
39403             } else if (win.document.selection && win.document.selection.createRange) {
39404                 // no firefox support
39405                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39406                 win.document.selection.createRange().pasteHTML(txt);
39407             } else {
39408                 // no firefox support
39409                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39410                 this.execCmd('InsertHTML', txt);
39411             } 
39412             
39413             this.syncValue();
39414             
39415             this.deferFocus();
39416         }
39417     },
39418  // private
39419     mozKeyPress : function(e){
39420         if(e.ctrlKey){
39421             var c = e.getCharCode(), cmd;
39422           
39423             if(c > 0){
39424                 c = String.fromCharCode(c).toLowerCase();
39425                 switch(c){
39426                     case 'b':
39427                         cmd = 'bold';
39428                         break;
39429                     case 'i':
39430                         cmd = 'italic';
39431                         break;
39432                     
39433                     case 'u':
39434                         cmd = 'underline';
39435                         break;
39436                     
39437                     case 'v':
39438                         this.cleanUpPaste.defer(100, this);
39439                         return;
39440                         
39441                 }
39442                 if(cmd){
39443                     this.win.focus();
39444                     this.execCmd(cmd);
39445                     this.deferFocus();
39446                     e.preventDefault();
39447                 }
39448                 
39449             }
39450         }
39451     },
39452
39453     // private
39454     fixKeys : function(){ // load time branching for fastest keydown performance
39455         if(Roo.isIE){
39456             return function(e){
39457                 var k = e.getKey(), r;
39458                 if(k == e.TAB){
39459                     e.stopEvent();
39460                     r = this.doc.selection.createRange();
39461                     if(r){
39462                         r.collapse(true);
39463                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39464                         this.deferFocus();
39465                     }
39466                     return;
39467                 }
39468                 
39469                 if(k == e.ENTER){
39470                     r = this.doc.selection.createRange();
39471                     if(r){
39472                         var target = r.parentElement();
39473                         if(!target || target.tagName.toLowerCase() != 'li'){
39474                             e.stopEvent();
39475                             r.pasteHTML('<br />');
39476                             r.collapse(false);
39477                             r.select();
39478                         }
39479                     }
39480                 }
39481                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39482                     this.cleanUpPaste.defer(100, this);
39483                     return;
39484                 }
39485                 
39486                 
39487             };
39488         }else if(Roo.isOpera){
39489             return function(e){
39490                 var k = e.getKey();
39491                 if(k == e.TAB){
39492                     e.stopEvent();
39493                     this.win.focus();
39494                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39495                     this.deferFocus();
39496                 }
39497                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39498                     this.cleanUpPaste.defer(100, this);
39499                     return;
39500                 }
39501                 
39502             };
39503         }else if(Roo.isSafari){
39504             return function(e){
39505                 var k = e.getKey();
39506                 
39507                 if(k == e.TAB){
39508                     e.stopEvent();
39509                     this.execCmd('InsertText','\t');
39510                     this.deferFocus();
39511                     return;
39512                 }
39513                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39514                     this.cleanUpPaste.defer(100, this);
39515                     return;
39516                 }
39517                 
39518              };
39519         }
39520     }(),
39521     
39522     getAllAncestors: function()
39523     {
39524         var p = this.getSelectedNode();
39525         var a = [];
39526         if (!p) {
39527             a.push(p); // push blank onto stack..
39528             p = this.getParentElement();
39529         }
39530         
39531         
39532         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39533             a.push(p);
39534             p = p.parentNode;
39535         }
39536         a.push(this.doc.body);
39537         return a;
39538     },
39539     lastSel : false,
39540     lastSelNode : false,
39541     
39542     
39543     getSelection : function() 
39544     {
39545         this.assignDocWin();
39546         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39547     },
39548     
39549     getSelectedNode: function() 
39550     {
39551         // this may only work on Gecko!!!
39552         
39553         // should we cache this!!!!
39554         
39555         
39556         
39557          
39558         var range = this.createRange(this.getSelection()).cloneRange();
39559         
39560         if (Roo.isIE) {
39561             var parent = range.parentElement();
39562             while (true) {
39563                 var testRange = range.duplicate();
39564                 testRange.moveToElementText(parent);
39565                 if (testRange.inRange(range)) {
39566                     break;
39567                 }
39568                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39569                     break;
39570                 }
39571                 parent = parent.parentElement;
39572             }
39573             return parent;
39574         }
39575         
39576         // is ancestor a text element.
39577         var ac =  range.commonAncestorContainer;
39578         if (ac.nodeType == 3) {
39579             ac = ac.parentNode;
39580         }
39581         
39582         var ar = ac.childNodes;
39583          
39584         var nodes = [];
39585         var other_nodes = [];
39586         var has_other_nodes = false;
39587         for (var i=0;i<ar.length;i++) {
39588             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39589                 continue;
39590             }
39591             // fullly contained node.
39592             
39593             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39594                 nodes.push(ar[i]);
39595                 continue;
39596             }
39597             
39598             // probably selected..
39599             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39600                 other_nodes.push(ar[i]);
39601                 continue;
39602             }
39603             // outer..
39604             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39605                 continue;
39606             }
39607             
39608             
39609             has_other_nodes = true;
39610         }
39611         if (!nodes.length && other_nodes.length) {
39612             nodes= other_nodes;
39613         }
39614         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39615             return false;
39616         }
39617         
39618         return nodes[0];
39619     },
39620     createRange: function(sel)
39621     {
39622         // this has strange effects when using with 
39623         // top toolbar - not sure if it's a great idea.
39624         //this.editor.contentWindow.focus();
39625         if (typeof sel != "undefined") {
39626             try {
39627                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39628             } catch(e) {
39629                 return this.doc.createRange();
39630             }
39631         } else {
39632             return this.doc.createRange();
39633         }
39634     },
39635     getParentElement: function()
39636     {
39637         
39638         this.assignDocWin();
39639         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39640         
39641         var range = this.createRange(sel);
39642          
39643         try {
39644             var p = range.commonAncestorContainer;
39645             while (p.nodeType == 3) { // text node
39646                 p = p.parentNode;
39647             }
39648             return p;
39649         } catch (e) {
39650             return null;
39651         }
39652     
39653     },
39654     /***
39655      *
39656      * Range intersection.. the hard stuff...
39657      *  '-1' = before
39658      *  '0' = hits..
39659      *  '1' = after.
39660      *         [ -- selected range --- ]
39661      *   [fail]                        [fail]
39662      *
39663      *    basically..
39664      *      if end is before start or  hits it. fail.
39665      *      if start is after end or hits it fail.
39666      *
39667      *   if either hits (but other is outside. - then it's not 
39668      *   
39669      *    
39670      **/
39671     
39672     
39673     // @see http://www.thismuchiknow.co.uk/?p=64.
39674     rangeIntersectsNode : function(range, node)
39675     {
39676         var nodeRange = node.ownerDocument.createRange();
39677         try {
39678             nodeRange.selectNode(node);
39679         } catch (e) {
39680             nodeRange.selectNodeContents(node);
39681         }
39682     
39683         var rangeStartRange = range.cloneRange();
39684         rangeStartRange.collapse(true);
39685     
39686         var rangeEndRange = range.cloneRange();
39687         rangeEndRange.collapse(false);
39688     
39689         var nodeStartRange = nodeRange.cloneRange();
39690         nodeStartRange.collapse(true);
39691     
39692         var nodeEndRange = nodeRange.cloneRange();
39693         nodeEndRange.collapse(false);
39694     
39695         return rangeStartRange.compareBoundaryPoints(
39696                  Range.START_TO_START, nodeEndRange) == -1 &&
39697                rangeEndRange.compareBoundaryPoints(
39698                  Range.START_TO_START, nodeStartRange) == 1;
39699         
39700          
39701     },
39702     rangeCompareNode : function(range, node)
39703     {
39704         var nodeRange = node.ownerDocument.createRange();
39705         try {
39706             nodeRange.selectNode(node);
39707         } catch (e) {
39708             nodeRange.selectNodeContents(node);
39709         }
39710         
39711         
39712         range.collapse(true);
39713     
39714         nodeRange.collapse(true);
39715      
39716         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39717         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39718          
39719         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39720         
39721         var nodeIsBefore   =  ss == 1;
39722         var nodeIsAfter    = ee == -1;
39723         
39724         if (nodeIsBefore && nodeIsAfter)
39725             return 0; // outer
39726         if (!nodeIsBefore && nodeIsAfter)
39727             return 1; //right trailed.
39728         
39729         if (nodeIsBefore && !nodeIsAfter)
39730             return 2;  // left trailed.
39731         // fully contined.
39732         return 3;
39733     },
39734
39735     // private? - in a new class?
39736     cleanUpPaste :  function()
39737     {
39738         // cleans up the whole document..
39739          Roo.log('cleanuppaste');
39740         this.cleanUpChildren(this.doc.body);
39741         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39742         if (clean != this.doc.body.innerHTML) {
39743             this.doc.body.innerHTML = clean;
39744         }
39745         
39746     },
39747     
39748     cleanWordChars : function(input) {
39749         var he = Roo.form.HtmlEditor;
39750     
39751         var output = input;
39752         Roo.each(he.swapCodes, function(sw) { 
39753         
39754             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39755             output = output.replace(swapper, sw[1]);
39756         });
39757         return output;
39758     },
39759     
39760     
39761     cleanUpChildren : function (n)
39762     {
39763         if (!n.childNodes.length) {
39764             return;
39765         }
39766         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39767            this.cleanUpChild(n.childNodes[i]);
39768         }
39769     },
39770     
39771     
39772         
39773     
39774     cleanUpChild : function (node)
39775     {
39776         //console.log(node);
39777         if (node.nodeName == "#text") {
39778             // clean up silly Windows -- stuff?
39779             return; 
39780         }
39781         if (node.nodeName == "#comment") {
39782             node.parentNode.removeChild(node);
39783             // clean up silly Windows -- stuff?
39784             return; 
39785         }
39786         
39787         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39788             // remove node.
39789             node.parentNode.removeChild(node);
39790             return;
39791             
39792         }
39793         
39794         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39795         
39796         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39797         
39798         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39799             remove_keep_children = true;
39800         }
39801         
39802         if (remove_keep_children) {
39803             this.cleanUpChildren(node);
39804             // inserts everything just before this node...
39805             while (node.childNodes.length) {
39806                 var cn = node.childNodes[0];
39807                 node.removeChild(cn);
39808                 node.parentNode.insertBefore(cn, node);
39809             }
39810             node.parentNode.removeChild(node);
39811             return;
39812         }
39813         
39814         if (!node.attributes || !node.attributes.length) {
39815             this.cleanUpChildren(node);
39816             return;
39817         }
39818         
39819         function cleanAttr(n,v)
39820         {
39821             
39822             if (v.match(/^\./) || v.match(/^\//)) {
39823                 return;
39824             }
39825             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39826                 return;
39827             }
39828             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39829             node.removeAttribute(n);
39830             
39831         }
39832         
39833         function cleanStyle(n,v)
39834         {
39835             if (v.match(/expression/)) { //XSS?? should we even bother..
39836                 node.removeAttribute(n);
39837                 return;
39838             }
39839             
39840             
39841             var parts = v.split(/;/);
39842             Roo.each(parts, function(p) {
39843                 p = p.replace(/\s+/g,'');
39844                 if (!p.length) {
39845                     return true;
39846                 }
39847                 var l = p.split(':').shift().replace(/\s+/g,'');
39848                 
39849                 // only allow 'c whitelisted system attributes'
39850                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39851                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39852                     node.removeAttribute(n);
39853                     return false;
39854                 }
39855                 return true;
39856             });
39857             
39858             
39859         }
39860         
39861         
39862         for (var i = node.attributes.length-1; i > -1 ; i--) {
39863             var a = node.attributes[i];
39864             //console.log(a);
39865             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39866                 node.removeAttribute(a.name);
39867                 return;
39868             }
39869             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39870                 cleanAttr(a.name,a.value); // fixme..
39871                 return;
39872             }
39873             if (a.name == 'style') {
39874                 cleanStyle(a.name,a.value);
39875             }
39876             /// clean up MS crap..
39877             // tecnically this should be a list of valid class'es..
39878             
39879             
39880             if (a.name == 'class') {
39881                 if (a.value.match(/^Mso/)) {
39882                     node.className = '';
39883                 }
39884                 
39885                 if (a.value.match(/body/)) {
39886                     node.className = '';
39887                 }
39888             }
39889             
39890             // style cleanup!?
39891             // class cleanup?
39892             
39893         }
39894         
39895         
39896         this.cleanUpChildren(node);
39897         
39898         
39899     }
39900     
39901     
39902     // hide stuff that is not compatible
39903     /**
39904      * @event blur
39905      * @hide
39906      */
39907     /**
39908      * @event change
39909      * @hide
39910      */
39911     /**
39912      * @event focus
39913      * @hide
39914      */
39915     /**
39916      * @event specialkey
39917      * @hide
39918      */
39919     /**
39920      * @cfg {String} fieldClass @hide
39921      */
39922     /**
39923      * @cfg {String} focusClass @hide
39924      */
39925     /**
39926      * @cfg {String} autoCreate @hide
39927      */
39928     /**
39929      * @cfg {String} inputType @hide
39930      */
39931     /**
39932      * @cfg {String} invalidClass @hide
39933      */
39934     /**
39935      * @cfg {String} invalidText @hide
39936      */
39937     /**
39938      * @cfg {String} msgFx @hide
39939      */
39940     /**
39941      * @cfg {String} validateOnBlur @hide
39942      */
39943 });
39944
39945 Roo.form.HtmlEditor.white = [
39946         'area', 'br', 'img', 'input', 'hr', 'wbr',
39947         
39948        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39949        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39950        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39951        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39952        'table',   'ul',         'xmp', 
39953        
39954        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39955       'thead',   'tr', 
39956      
39957       'dir', 'menu', 'ol', 'ul', 'dl',
39958        
39959       'embed',  'object'
39960 ];
39961
39962
39963 Roo.form.HtmlEditor.black = [
39964     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39965         'applet', // 
39966         'base',   'basefont', 'bgsound', 'blink',  'body', 
39967         'frame',  'frameset', 'head',    'html',   'ilayer', 
39968         'iframe', 'layer',  'link',     'meta',    'object',   
39969         'script', 'style' ,'title',  'xml' // clean later..
39970 ];
39971 Roo.form.HtmlEditor.clean = [
39972     'script', 'style', 'title', 'xml'
39973 ];
39974 Roo.form.HtmlEditor.remove = [
39975     'font'
39976 ];
39977 // attributes..
39978
39979 Roo.form.HtmlEditor.ablack = [
39980     'on'
39981 ];
39982     
39983 Roo.form.HtmlEditor.aclean = [ 
39984     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39985 ];
39986
39987 // protocols..
39988 Roo.form.HtmlEditor.pwhite= [
39989         'http',  'https',  'mailto'
39990 ];
39991
39992 // white listed style attributes.
39993 Roo.form.HtmlEditor.cwhite= [
39994         'text-align',
39995         'font-size'
39996 ];
39997
39998
39999 Roo.form.HtmlEditor.swapCodes   =[ 
40000     [    8211, "--" ], 
40001     [    8212, "--" ], 
40002     [    8216,  "'" ],  
40003     [    8217, "'" ],  
40004     [    8220, '"' ],  
40005     [    8221, '"' ],  
40006     [    8226, "*" ],  
40007     [    8230, "..." ]
40008 ]; 
40009
40010     // <script type="text/javascript">
40011 /*
40012  * Based on
40013  * Ext JS Library 1.1.1
40014  * Copyright(c) 2006-2007, Ext JS, LLC.
40015  *  
40016  
40017  */
40018
40019 /**
40020  * @class Roo.form.HtmlEditorToolbar1
40021  * Basic Toolbar
40022  * 
40023  * Usage:
40024  *
40025  new Roo.form.HtmlEditor({
40026     ....
40027     toolbars : [
40028         new Roo.form.HtmlEditorToolbar1({
40029             disable : { fonts: 1 , format: 1, ..., ... , ...],
40030             btns : [ .... ]
40031         })
40032     }
40033      
40034  * 
40035  * @cfg {Object} disable List of elements to disable..
40036  * @cfg {Array} btns List of additional buttons.
40037  * 
40038  * 
40039  * NEEDS Extra CSS? 
40040  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
40041  */
40042  
40043 Roo.form.HtmlEditor.ToolbarStandard = function(config)
40044 {
40045     
40046     Roo.apply(this, config);
40047     
40048     // default disabled, based on 'good practice'..
40049     this.disable = this.disable || {};
40050     Roo.applyIf(this.disable, {
40051         fontSize : true,
40052         colors : true,
40053         specialElements : true
40054     });
40055     
40056     
40057     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40058     // dont call parent... till later.
40059 }
40060
40061 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
40062     
40063     tb: false,
40064     
40065     rendered: false,
40066     
40067     editor : false,
40068     /**
40069      * @cfg {Object} disable  List of toolbar elements to disable
40070          
40071      */
40072     disable : false,
40073       /**
40074      * @cfg {Array} fontFamilies An array of available font families
40075      */
40076     fontFamilies : [
40077         'Arial',
40078         'Courier New',
40079         'Tahoma',
40080         'Times New Roman',
40081         'Verdana'
40082     ],
40083     
40084     specialChars : [
40085            "&#169;",
40086           "&#174;",     
40087           "&#8482;",    
40088           "&#163;" ,    
40089          // "&#8212;",    
40090           "&#8230;",    
40091           "&#247;" ,    
40092         //  "&#225;" ,     ?? a acute?
40093            "&#8364;"    , //Euro
40094        //   "&#8220;"    ,
40095         //  "&#8221;"    ,
40096         //  "&#8226;"    ,
40097           "&#176;"  //   , // degrees
40098
40099          // "&#233;"     , // e ecute
40100          // "&#250;"     , // u ecute?
40101     ],
40102     
40103     specialElements : [
40104         {
40105             text: "Insert Table",
40106             xtype: 'MenuItem',
40107             xns : Roo.Menu,
40108             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
40109                 
40110         },
40111         {    
40112             text: "Insert Image",
40113             xtype: 'MenuItem',
40114             xns : Roo.Menu,
40115             ihtml : '<img src="about:blank"/>'
40116             
40117         }
40118         
40119          
40120     ],
40121     
40122     
40123     inputElements : [ 
40124             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40125             "input:submit", "input:button", "select", "textarea", "label" ],
40126     formats : [
40127         ["p"] ,  
40128         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40129         ["pre"],[ "code"], 
40130         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40131     ],
40132      /**
40133      * @cfg {String} defaultFont default font to use.
40134      */
40135     defaultFont: 'tahoma',
40136    
40137     fontSelect : false,
40138     
40139     
40140     formatCombo : false,
40141     
40142     init : function(editor)
40143     {
40144         this.editor = editor;
40145         
40146         
40147         var fid = editor.frameId;
40148         var etb = this;
40149         function btn(id, toggle, handler){
40150             var xid = fid + '-'+ id ;
40151             return {
40152                 id : xid,
40153                 cmd : id,
40154                 cls : 'x-btn-icon x-edit-'+id,
40155                 enableToggle:toggle !== false,
40156                 scope: editor, // was editor...
40157                 handler:handler||editor.relayBtnCmd,
40158                 clickEvent:'mousedown',
40159                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40160                 tabIndex:-1
40161             };
40162         }
40163         
40164         
40165         
40166         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40167         this.tb = tb;
40168          // stop form submits
40169         tb.el.on('click', function(e){
40170             e.preventDefault(); // what does this do?
40171         });
40172
40173         if(!this.disable.font && !Roo.isSafari){
40174             /* why no safari for fonts
40175             editor.fontSelect = tb.el.createChild({
40176                 tag:'select',
40177                 tabIndex: -1,
40178                 cls:'x-font-select',
40179                 html: editor.createFontOptions()
40180             });
40181             editor.fontSelect.on('change', function(){
40182                 var font = editor.fontSelect.dom.value;
40183                 editor.relayCmd('fontname', font);
40184                 editor.deferFocus();
40185             }, editor);
40186             tb.add(
40187                 editor.fontSelect.dom,
40188                 '-'
40189             );
40190             */
40191         };
40192         if(!this.disable.formats){
40193             this.formatCombo = new Roo.form.ComboBox({
40194                 store: new Roo.data.SimpleStore({
40195                     id : 'tag',
40196                     fields: ['tag'],
40197                     data : this.formats // from states.js
40198                 }),
40199                 blockFocus : true,
40200                 //autoCreate : {tag: "div",  size: "20"},
40201                 displayField:'tag',
40202                 typeAhead: false,
40203                 mode: 'local',
40204                 editable : false,
40205                 triggerAction: 'all',
40206                 emptyText:'Add tag',
40207                 selectOnFocus:true,
40208                 width:135,
40209                 listeners : {
40210                     'select': function(c, r, i) {
40211                         editor.insertTag(r.get('tag'));
40212                         editor.focus();
40213                     }
40214                 }
40215
40216             });
40217             tb.addField(this.formatCombo);
40218             
40219         }
40220         
40221         if(!this.disable.format){
40222             tb.add(
40223                 btn('bold'),
40224                 btn('italic'),
40225                 btn('underline')
40226             );
40227         };
40228         if(!this.disable.fontSize){
40229             tb.add(
40230                 '-',
40231                 
40232                 
40233                 btn('increasefontsize', false, editor.adjustFont),
40234                 btn('decreasefontsize', false, editor.adjustFont)
40235             );
40236         };
40237         
40238         
40239         if(!this.disable.colors){
40240             tb.add(
40241                 '-', {
40242                     id:editor.frameId +'-forecolor',
40243                     cls:'x-btn-icon x-edit-forecolor',
40244                     clickEvent:'mousedown',
40245                     tooltip: this.buttonTips['forecolor'] || undefined,
40246                     tabIndex:-1,
40247                     menu : new Roo.menu.ColorMenu({
40248                         allowReselect: true,
40249                         focus: Roo.emptyFn,
40250                         value:'000000',
40251                         plain:true,
40252                         selectHandler: function(cp, color){
40253                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40254                             editor.deferFocus();
40255                         },
40256                         scope: editor,
40257                         clickEvent:'mousedown'
40258                     })
40259                 }, {
40260                     id:editor.frameId +'backcolor',
40261                     cls:'x-btn-icon x-edit-backcolor',
40262                     clickEvent:'mousedown',
40263                     tooltip: this.buttonTips['backcolor'] || undefined,
40264                     tabIndex:-1,
40265                     menu : new Roo.menu.ColorMenu({
40266                         focus: Roo.emptyFn,
40267                         value:'FFFFFF',
40268                         plain:true,
40269                         allowReselect: true,
40270                         selectHandler: function(cp, color){
40271                             if(Roo.isGecko){
40272                                 editor.execCmd('useCSS', false);
40273                                 editor.execCmd('hilitecolor', color);
40274                                 editor.execCmd('useCSS', true);
40275                                 editor.deferFocus();
40276                             }else{
40277                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40278                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40279                                 editor.deferFocus();
40280                             }
40281                         },
40282                         scope:editor,
40283                         clickEvent:'mousedown'
40284                     })
40285                 }
40286             );
40287         };
40288         // now add all the items...
40289         
40290
40291         if(!this.disable.alignments){
40292             tb.add(
40293                 '-',
40294                 btn('justifyleft'),
40295                 btn('justifycenter'),
40296                 btn('justifyright')
40297             );
40298         };
40299
40300         //if(!Roo.isSafari){
40301             if(!this.disable.links){
40302                 tb.add(
40303                     '-',
40304                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40305                 );
40306             };
40307
40308             if(!this.disable.lists){
40309                 tb.add(
40310                     '-',
40311                     btn('insertorderedlist'),
40312                     btn('insertunorderedlist')
40313                 );
40314             }
40315             if(!this.disable.sourceEdit){
40316                 tb.add(
40317                     '-',
40318                     btn('sourceedit', true, function(btn){
40319                         this.toggleSourceEdit(btn.pressed);
40320                     })
40321                 );
40322             }
40323         //}
40324         
40325         var smenu = { };
40326         // special menu.. - needs to be tidied up..
40327         if (!this.disable.special) {
40328             smenu = {
40329                 text: "&#169;",
40330                 cls: 'x-edit-none',
40331                 
40332                 menu : {
40333                     items : []
40334                 }
40335             };
40336             for (var i =0; i < this.specialChars.length; i++) {
40337                 smenu.menu.items.push({
40338                     
40339                     html: this.specialChars[i],
40340                     handler: function(a,b) {
40341                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40342                         //editor.insertAtCursor(a.html);
40343                         
40344                     },
40345                     tabIndex:-1
40346                 });
40347             }
40348             
40349             
40350             tb.add(smenu);
40351             
40352             
40353         }
40354          
40355         if (!this.disable.specialElements) {
40356             var semenu = {
40357                 text: "Other;",
40358                 cls: 'x-edit-none',
40359                 menu : {
40360                     items : []
40361                 }
40362             };
40363             for (var i =0; i < this.specialElements.length; i++) {
40364                 semenu.menu.items.push(
40365                     Roo.apply({ 
40366                         handler: function(a,b) {
40367                             editor.insertAtCursor(this.ihtml);
40368                         }
40369                     }, this.specialElements[i])
40370                 );
40371                     
40372             }
40373             
40374             tb.add(semenu);
40375             
40376             
40377         }
40378          
40379         
40380         if (this.btns) {
40381             for(var i =0; i< this.btns.length;i++) {
40382                 var b = this.btns[i];
40383                 b.cls =  'x-edit-none';
40384                 b.scope = editor;
40385                 tb.add(b);
40386             }
40387         
40388         }
40389         
40390         
40391         
40392         // disable everything...
40393         
40394         this.tb.items.each(function(item){
40395            if(item.id != editor.frameId+ '-sourceedit'){
40396                 item.disable();
40397             }
40398         });
40399         this.rendered = true;
40400         
40401         // the all the btns;
40402         editor.on('editorevent', this.updateToolbar, this);
40403         // other toolbars need to implement this..
40404         //editor.on('editmodechange', this.updateToolbar, this);
40405     },
40406     
40407     
40408     
40409     /**
40410      * Protected method that will not generally be called directly. It triggers
40411      * a toolbar update by reading the markup state of the current selection in the editor.
40412      */
40413     updateToolbar: function(){
40414
40415         if(!this.editor.activated){
40416             this.editor.onFirstFocus();
40417             return;
40418         }
40419
40420         var btns = this.tb.items.map, 
40421             doc = this.editor.doc,
40422             frameId = this.editor.frameId;
40423
40424         if(!this.disable.font && !Roo.isSafari){
40425             /*
40426             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40427             if(name != this.fontSelect.dom.value){
40428                 this.fontSelect.dom.value = name;
40429             }
40430             */
40431         }
40432         if(!this.disable.format){
40433             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40434             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40435             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40436         }
40437         if(!this.disable.alignments){
40438             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40439             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40440             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40441         }
40442         if(!Roo.isSafari && !this.disable.lists){
40443             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40444             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40445         }
40446         
40447         var ans = this.editor.getAllAncestors();
40448         if (this.formatCombo) {
40449             
40450             
40451             var store = this.formatCombo.store;
40452             this.formatCombo.setValue("");
40453             for (var i =0; i < ans.length;i++) {
40454                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40455                     // select it..
40456                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40457                     break;
40458                 }
40459             }
40460         }
40461         
40462         
40463         
40464         // hides menus... - so this cant be on a menu...
40465         Roo.menu.MenuMgr.hideAll();
40466
40467         //this.editorsyncValue();
40468     },
40469    
40470     
40471     createFontOptions : function(){
40472         var buf = [], fs = this.fontFamilies, ff, lc;
40473         for(var i = 0, len = fs.length; i< len; i++){
40474             ff = fs[i];
40475             lc = ff.toLowerCase();
40476             buf.push(
40477                 '<option value="',lc,'" style="font-family:',ff,';"',
40478                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40479                     ff,
40480                 '</option>'
40481             );
40482         }
40483         return buf.join('');
40484     },
40485     
40486     toggleSourceEdit : function(sourceEditMode){
40487         if(sourceEditMode === undefined){
40488             sourceEditMode = !this.sourceEditMode;
40489         }
40490         this.sourceEditMode = sourceEditMode === true;
40491         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40492         // just toggle the button?
40493         if(btn.pressed !== this.editor.sourceEditMode){
40494             btn.toggle(this.editor.sourceEditMode);
40495             return;
40496         }
40497         
40498         if(this.sourceEditMode){
40499             this.tb.items.each(function(item){
40500                 if(item.cmd != 'sourceedit'){
40501                     item.disable();
40502                 }
40503             });
40504           
40505         }else{
40506             if(this.initialized){
40507                 this.tb.items.each(function(item){
40508                     item.enable();
40509                 });
40510             }
40511             
40512         }
40513         // tell the editor that it's been pressed..
40514         this.editor.toggleSourceEdit(sourceEditMode);
40515        
40516     },
40517      /**
40518      * Object collection of toolbar tooltips for the buttons in the editor. The key
40519      * is the command id associated with that button and the value is a valid QuickTips object.
40520      * For example:
40521 <pre><code>
40522 {
40523     bold : {
40524         title: 'Bold (Ctrl+B)',
40525         text: 'Make the selected text bold.',
40526         cls: 'x-html-editor-tip'
40527     },
40528     italic : {
40529         title: 'Italic (Ctrl+I)',
40530         text: 'Make the selected text italic.',
40531         cls: 'x-html-editor-tip'
40532     },
40533     ...
40534 </code></pre>
40535     * @type Object
40536      */
40537     buttonTips : {
40538         bold : {
40539             title: 'Bold (Ctrl+B)',
40540             text: 'Make the selected text bold.',
40541             cls: 'x-html-editor-tip'
40542         },
40543         italic : {
40544             title: 'Italic (Ctrl+I)',
40545             text: 'Make the selected text italic.',
40546             cls: 'x-html-editor-tip'
40547         },
40548         underline : {
40549             title: 'Underline (Ctrl+U)',
40550             text: 'Underline the selected text.',
40551             cls: 'x-html-editor-tip'
40552         },
40553         increasefontsize : {
40554             title: 'Grow Text',
40555             text: 'Increase the font size.',
40556             cls: 'x-html-editor-tip'
40557         },
40558         decreasefontsize : {
40559             title: 'Shrink Text',
40560             text: 'Decrease the font size.',
40561             cls: 'x-html-editor-tip'
40562         },
40563         backcolor : {
40564             title: 'Text Highlight Color',
40565             text: 'Change the background color of the selected text.',
40566             cls: 'x-html-editor-tip'
40567         },
40568         forecolor : {
40569             title: 'Font Color',
40570             text: 'Change the color of the selected text.',
40571             cls: 'x-html-editor-tip'
40572         },
40573         justifyleft : {
40574             title: 'Align Text Left',
40575             text: 'Align text to the left.',
40576             cls: 'x-html-editor-tip'
40577         },
40578         justifycenter : {
40579             title: 'Center Text',
40580             text: 'Center text in the editor.',
40581             cls: 'x-html-editor-tip'
40582         },
40583         justifyright : {
40584             title: 'Align Text Right',
40585             text: 'Align text to the right.',
40586             cls: 'x-html-editor-tip'
40587         },
40588         insertunorderedlist : {
40589             title: 'Bullet List',
40590             text: 'Start a bulleted list.',
40591             cls: 'x-html-editor-tip'
40592         },
40593         insertorderedlist : {
40594             title: 'Numbered List',
40595             text: 'Start a numbered list.',
40596             cls: 'x-html-editor-tip'
40597         },
40598         createlink : {
40599             title: 'Hyperlink',
40600             text: 'Make the selected text a hyperlink.',
40601             cls: 'x-html-editor-tip'
40602         },
40603         sourceedit : {
40604             title: 'Source Edit',
40605             text: 'Switch to source editing mode.',
40606             cls: 'x-html-editor-tip'
40607         }
40608     },
40609     // private
40610     onDestroy : function(){
40611         if(this.rendered){
40612             
40613             this.tb.items.each(function(item){
40614                 if(item.menu){
40615                     item.menu.removeAll();
40616                     if(item.menu.el){
40617                         item.menu.el.destroy();
40618                     }
40619                 }
40620                 item.destroy();
40621             });
40622              
40623         }
40624     },
40625     onFirstFocus: function() {
40626         this.tb.items.each(function(item){
40627            item.enable();
40628         });
40629     }
40630 });
40631
40632
40633
40634
40635 // <script type="text/javascript">
40636 /*
40637  * Based on
40638  * Ext JS Library 1.1.1
40639  * Copyright(c) 2006-2007, Ext JS, LLC.
40640  *  
40641  
40642  */
40643
40644  
40645 /**
40646  * @class Roo.form.HtmlEditor.ToolbarContext
40647  * Context Toolbar
40648  * 
40649  * Usage:
40650  *
40651  new Roo.form.HtmlEditor({
40652     ....
40653     toolbars : [
40654         { xtype: 'ToolbarStandard', styles : {} }
40655         { xtype: 'ToolbarContext', disable : {} }
40656     ]
40657 })
40658
40659      
40660  * 
40661  * @config : {Object} disable List of elements to disable.. (not done yet.)
40662  * @config : {Object} styles  Map of styles available.
40663  * 
40664  */
40665
40666 Roo.form.HtmlEditor.ToolbarContext = function(config)
40667 {
40668     
40669     Roo.apply(this, config);
40670     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40671     // dont call parent... till later.
40672     this.styles = this.styles || {};
40673 }
40674 Roo.form.HtmlEditor.ToolbarContext.types = {
40675     'IMG' : {
40676         width : {
40677             title: "Width",
40678             width: 40
40679         },
40680         height:  {
40681             title: "Height",
40682             width: 40
40683         },
40684         align: {
40685             title: "Align",
40686             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40687             width : 80
40688             
40689         },
40690         border: {
40691             title: "Border",
40692             width: 40
40693         },
40694         alt: {
40695             title: "Alt",
40696             width: 120
40697         },
40698         src : {
40699             title: "Src",
40700             width: 220
40701         }
40702         
40703     },
40704     'A' : {
40705         name : {
40706             title: "Name",
40707             width: 50
40708         },
40709         href:  {
40710             title: "Href",
40711             width: 220
40712         } // border?
40713         
40714     },
40715     'TABLE' : {
40716         rows : {
40717             title: "Rows",
40718             width: 20
40719         },
40720         cols : {
40721             title: "Cols",
40722             width: 20
40723         },
40724         width : {
40725             title: "Width",
40726             width: 40
40727         },
40728         height : {
40729             title: "Height",
40730             width: 40
40731         },
40732         border : {
40733             title: "Border",
40734             width: 20
40735         }
40736     },
40737     'TD' : {
40738         width : {
40739             title: "Width",
40740             width: 40
40741         },
40742         height : {
40743             title: "Height",
40744             width: 40
40745         },   
40746         align: {
40747             title: "Align",
40748             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40749             width: 80
40750         },
40751         valign: {
40752             title: "Valign",
40753             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40754             width: 80
40755         },
40756         colspan: {
40757             title: "Colspan",
40758             width: 20
40759             
40760         }
40761     },
40762     'INPUT' : {
40763         name : {
40764             title: "name",
40765             width: 120
40766         },
40767         value : {
40768             title: "Value",
40769             width: 120
40770         },
40771         width : {
40772             title: "Width",
40773             width: 40
40774         }
40775     },
40776     'LABEL' : {
40777         'for' : {
40778             title: "For",
40779             width: 120
40780         }
40781     },
40782     'TEXTAREA' : {
40783           name : {
40784             title: "name",
40785             width: 120
40786         },
40787         rows : {
40788             title: "Rows",
40789             width: 20
40790         },
40791         cols : {
40792             title: "Cols",
40793             width: 20
40794         }
40795     },
40796     'SELECT' : {
40797         name : {
40798             title: "name",
40799             width: 120
40800         },
40801         selectoptions : {
40802             title: "Options",
40803             width: 200
40804         }
40805     },
40806     
40807     // should we really allow this??
40808     // should this just be 
40809     'BODY' : {
40810         title : {
40811             title: "title",
40812             width: 200,
40813             disabled : true
40814         }
40815     },
40816     '*' : {
40817         // empty..
40818     }
40819 };
40820
40821
40822
40823 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40824     
40825     tb: false,
40826     
40827     rendered: false,
40828     
40829     editor : false,
40830     /**
40831      * @cfg {Object} disable  List of toolbar elements to disable
40832          
40833      */
40834     disable : false,
40835     /**
40836      * @cfg {Object} styles List of styles 
40837      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40838      *
40839      * These must be defined in the page, so they get rendered correctly..
40840      * .headline { }
40841      * TD.underline { }
40842      * 
40843      */
40844     styles : false,
40845     
40846     
40847     
40848     toolbars : false,
40849     
40850     init : function(editor)
40851     {
40852         this.editor = editor;
40853         
40854         
40855         var fid = editor.frameId;
40856         var etb = this;
40857         function btn(id, toggle, handler){
40858             var xid = fid + '-'+ id ;
40859             return {
40860                 id : xid,
40861                 cmd : id,
40862                 cls : 'x-btn-icon x-edit-'+id,
40863                 enableToggle:toggle !== false,
40864                 scope: editor, // was editor...
40865                 handler:handler||editor.relayBtnCmd,
40866                 clickEvent:'mousedown',
40867                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40868                 tabIndex:-1
40869             };
40870         }
40871         // create a new element.
40872         var wdiv = editor.wrap.createChild({
40873                 tag: 'div'
40874             }, editor.wrap.dom.firstChild.nextSibling, true);
40875         
40876         // can we do this more than once??
40877         
40878          // stop form submits
40879       
40880  
40881         // disable everything...
40882         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40883         this.toolbars = {};
40884            
40885         for (var i in  ty) {
40886           
40887             this.toolbars[i] = this.buildToolbar(ty[i],i);
40888         }
40889         this.tb = this.toolbars.BODY;
40890         this.tb.el.show();
40891         this.buildFooter();
40892         this.footer.show();
40893          
40894         this.rendered = true;
40895         
40896         // the all the btns;
40897         editor.on('editorevent', this.updateToolbar, this);
40898         // other toolbars need to implement this..
40899         //editor.on('editmodechange', this.updateToolbar, this);
40900     },
40901     
40902     
40903     
40904     /**
40905      * Protected method that will not generally be called directly. It triggers
40906      * a toolbar update by reading the markup state of the current selection in the editor.
40907      */
40908     updateToolbar: function(editor,ev,sel){
40909
40910         //Roo.log(ev);
40911         // capture mouse up - this is handy for selecting images..
40912         // perhaps should go somewhere else...
40913         if(!this.editor.activated){
40914              this.editor.onFirstFocus();
40915             return;
40916         }
40917         
40918         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
40919         // selectNode - might want to handle IE?
40920         if (ev &&
40921             (ev.type == 'mouseup' || ev.type == 'click' ) &&
40922             ev.target && ev.target.tagName == 'IMG') {
40923             // they have click on an image...
40924             // let's see if we can change the selection...
40925             sel = ev.target;
40926          
40927               var nodeRange = sel.ownerDocument.createRange();
40928             try {
40929                 nodeRange.selectNode(sel);
40930             } catch (e) {
40931                 nodeRange.selectNodeContents(sel);
40932             }
40933             //nodeRange.collapse(true);
40934             var s = editor.win.getSelection();
40935             s.removeAllRanges();
40936             s.addRange(nodeRange);
40937         }  
40938         
40939       
40940         var updateFooter = sel ? false : true;
40941         
40942         
40943         var ans = this.editor.getAllAncestors();
40944         
40945         // pick
40946         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40947         
40948         if (!sel) { 
40949             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40950             sel = sel ? sel : this.editor.doc.body;
40951             sel = sel.tagName.length ? sel : this.editor.doc.body;
40952             
40953         }
40954         // pick a menu that exists..
40955         var tn = sel.tagName.toUpperCase();
40956         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40957         
40958         tn = sel.tagName.toUpperCase();
40959         
40960         var lastSel = this.tb.selectedNode
40961         
40962         this.tb.selectedNode = sel;
40963         
40964         // if current menu does not match..
40965         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40966                 
40967             this.tb.el.hide();
40968             ///console.log("show: " + tn);
40969             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40970             this.tb.el.show();
40971             // update name
40972             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40973             
40974             
40975             // update attributes
40976             if (this.tb.fields) {
40977                 this.tb.fields.each(function(e) {
40978                    e.setValue(sel.getAttribute(e.name));
40979                 });
40980             }
40981             
40982             var hasStyles = false;
40983             for(var i in this.styles) {
40984                 hasStyles = true;
40985                 break;
40986             }
40987             
40988             // update styles
40989             if (hasStyles) { 
40990                 var st = this.tb.fields.item(0);
40991                 
40992                 st.store.removeAll();
40993                
40994                 
40995                 var cn = sel.className.split(/\s+/);
40996                 
40997                 var avs = [];
40998                 if (this.styles['*']) {
40999                     
41000                     Roo.each(this.styles['*'], function(v) {
41001                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41002                     });
41003                 }
41004                 if (this.styles[tn]) { 
41005                     Roo.each(this.styles[tn], function(v) {
41006                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41007                     });
41008                 }
41009                 
41010                 st.store.loadData(avs);
41011                 st.collapse();
41012                 st.setValue(cn);
41013             }
41014             // flag our selected Node.
41015             this.tb.selectedNode = sel;
41016            
41017            
41018             Roo.menu.MenuMgr.hideAll();
41019
41020         }
41021         
41022         if (!updateFooter) {
41023             return;
41024         }
41025         // update the footer
41026         //
41027         var html = '';
41028         
41029         this.footerEls = ans.reverse();
41030         Roo.each(this.footerEls, function(a,i) {
41031             if (!a) { return; }
41032             html += html.length ? ' &gt; '  :  '';
41033             
41034             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
41035             
41036         });
41037        
41038         // 
41039         var sz = this.footDisp.up('td').getSize();
41040         this.footDisp.dom.style.width = (sz.width -10) + 'px';
41041         this.footDisp.dom.style.marginLeft = '5px';
41042         
41043         this.footDisp.dom.style.overflow = 'hidden';
41044         
41045         this.footDisp.dom.innerHTML = html;
41046             
41047         //this.editorsyncValue();
41048     },
41049    
41050        
41051     // private
41052     onDestroy : function(){
41053         if(this.rendered){
41054             
41055             this.tb.items.each(function(item){
41056                 if(item.menu){
41057                     item.menu.removeAll();
41058                     if(item.menu.el){
41059                         item.menu.el.destroy();
41060                     }
41061                 }
41062                 item.destroy();
41063             });
41064              
41065         }
41066     },
41067     onFirstFocus: function() {
41068         // need to do this for all the toolbars..
41069         this.tb.items.each(function(item){
41070            item.enable();
41071         });
41072     },
41073     buildToolbar: function(tlist, nm)
41074     {
41075         var editor = this.editor;
41076          // create a new element.
41077         var wdiv = editor.wrap.createChild({
41078                 tag: 'div'
41079             }, editor.wrap.dom.firstChild.nextSibling, true);
41080         
41081        
41082         var tb = new Roo.Toolbar(wdiv);
41083         // add the name..
41084         
41085         tb.add(nm+ ":&nbsp;");
41086         
41087         var styles = [];
41088         for(var i in this.styles) {
41089             styles.push(i);
41090         }
41091         
41092         // styles...
41093         if (styles && styles.length) {
41094             
41095             // this needs a multi-select checkbox...
41096             tb.addField( new Roo.form.ComboBox({
41097                 store: new Roo.data.SimpleStore({
41098                     id : 'val',
41099                     fields: ['val', 'selected'],
41100                     data : [] 
41101                 }),
41102                 name : 'className',
41103                 displayField:'val',
41104                 typeAhead: false,
41105                 mode: 'local',
41106                 editable : false,
41107                 triggerAction: 'all',
41108                 emptyText:'Select Style',
41109                 selectOnFocus:true,
41110                 width: 130,
41111                 listeners : {
41112                     'select': function(c, r, i) {
41113                         // initial support only for on class per el..
41114                         tb.selectedNode.className =  r ? r.get('val') : '';
41115                         editor.syncValue();
41116                     }
41117                 }
41118     
41119             }));
41120         }
41121             
41122         
41123         
41124         for (var i in tlist) {
41125             
41126             var item = tlist[i];
41127             tb.add(item.title + ":&nbsp;");
41128             
41129             
41130             
41131             
41132             if (item.opts) {
41133                 // opts == pulldown..
41134                 tb.addField( new Roo.form.ComboBox({
41135                     store: new Roo.data.SimpleStore({
41136                         id : 'val',
41137                         fields: ['val'],
41138                         data : item.opts  
41139                     }),
41140                     name : i,
41141                     displayField:'val',
41142                     typeAhead: false,
41143                     mode: 'local',
41144                     editable : false,
41145                     triggerAction: 'all',
41146                     emptyText:'Select',
41147                     selectOnFocus:true,
41148                     width: item.width ? item.width  : 130,
41149                     listeners : {
41150                         'select': function(c, r, i) {
41151                             tb.selectedNode.setAttribute(c.name, r.get('val'));
41152                         }
41153                     }
41154
41155                 }));
41156                 continue;
41157                     
41158                  
41159                 
41160                 tb.addField( new Roo.form.TextField({
41161                     name: i,
41162                     width: 100,
41163                     //allowBlank:false,
41164                     value: ''
41165                 }));
41166                 continue;
41167             }
41168             tb.addField( new Roo.form.TextField({
41169                 name: i,
41170                 width: item.width,
41171                 //allowBlank:true,
41172                 value: '',
41173                 listeners: {
41174                     'change' : function(f, nv, ov) {
41175                         tb.selectedNode.setAttribute(f.name, nv);
41176                     }
41177                 }
41178             }));
41179              
41180         }
41181         tb.el.on('click', function(e){
41182             e.preventDefault(); // what does this do?
41183         });
41184         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41185         tb.el.hide();
41186         tb.name = nm;
41187         // dont need to disable them... as they will get hidden
41188         return tb;
41189          
41190         
41191     },
41192     buildFooter : function()
41193     {
41194         
41195         var fel = this.editor.wrap.createChild();
41196         this.footer = new Roo.Toolbar(fel);
41197         // toolbar has scrolly on left / right?
41198         var footDisp= new Roo.Toolbar.Fill();
41199         var _t = this;
41200         this.footer.add(
41201             {
41202                 text : '&lt;',
41203                 xtype: 'Button',
41204                 handler : function() {
41205                     _t.footDisp.scrollTo('left',0,true)
41206                 }
41207             }
41208         );
41209         this.footer.add( footDisp );
41210         this.footer.add( 
41211             {
41212                 text : '&gt;',
41213                 xtype: 'Button',
41214                 handler : function() {
41215                     // no animation..
41216                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41217                 }
41218             }
41219         );
41220         var fel = Roo.get(footDisp.el);
41221         fel.addClass('x-editor-context');
41222         this.footDispWrap = fel; 
41223         this.footDispWrap.overflow  = 'hidden';
41224         
41225         this.footDisp = fel.createChild();
41226         this.footDispWrap.on('click', this.onContextClick, this)
41227         
41228         
41229     },
41230     onContextClick : function (ev,dom)
41231     {
41232         ev.preventDefault();
41233         var  cn = dom.className;
41234         Roo.log(cn);
41235         if (!cn.match(/x-ed-loc-/)) {
41236             return;
41237         }
41238         var n = cn.split('-').pop();
41239         var ans = this.footerEls;
41240         var sel = ans[n];
41241         
41242          // pick
41243         var range = this.editor.createRange();
41244         
41245         range.selectNodeContents(sel);
41246         //range.selectNode(sel);
41247         
41248         
41249         var selection = this.editor.getSelection();
41250         selection.removeAllRanges();
41251         selection.addRange(range);
41252         
41253         
41254         
41255         this.updateToolbar(null, null, sel);
41256         
41257         
41258     }
41259     
41260     
41261     
41262     
41263     
41264 });
41265
41266
41267
41268
41269
41270 /*
41271  * Based on:
41272  * Ext JS Library 1.1.1
41273  * Copyright(c) 2006-2007, Ext JS, LLC.
41274  *
41275  * Originally Released Under LGPL - original licence link has changed is not relivant.
41276  *
41277  * Fork - LGPL
41278  * <script type="text/javascript">
41279  */
41280  
41281 /**
41282  * @class Roo.form.BasicForm
41283  * @extends Roo.util.Observable
41284  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41285  * @constructor
41286  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41287  * @param {Object} config Configuration options
41288  */
41289 Roo.form.BasicForm = function(el, config){
41290     this.allItems = [];
41291     this.childForms = [];
41292     Roo.apply(this, config);
41293     /*
41294      * The Roo.form.Field items in this form.
41295      * @type MixedCollection
41296      */
41297      
41298      
41299     this.items = new Roo.util.MixedCollection(false, function(o){
41300         return o.id || (o.id = Roo.id());
41301     });
41302     this.addEvents({
41303         /**
41304          * @event beforeaction
41305          * Fires before any action is performed. Return false to cancel the action.
41306          * @param {Form} this
41307          * @param {Action} action The action to be performed
41308          */
41309         beforeaction: true,
41310         /**
41311          * @event actionfailed
41312          * Fires when an action fails.
41313          * @param {Form} this
41314          * @param {Action} action The action that failed
41315          */
41316         actionfailed : true,
41317         /**
41318          * @event actioncomplete
41319          * Fires when an action is completed.
41320          * @param {Form} this
41321          * @param {Action} action The action that completed
41322          */
41323         actioncomplete : true
41324     });
41325     if(el){
41326         this.initEl(el);
41327     }
41328     Roo.form.BasicForm.superclass.constructor.call(this);
41329 };
41330
41331 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41332     /**
41333      * @cfg {String} method
41334      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41335      */
41336     /**
41337      * @cfg {DataReader} reader
41338      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41339      * This is optional as there is built-in support for processing JSON.
41340      */
41341     /**
41342      * @cfg {DataReader} errorReader
41343      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41344      * This is completely optional as there is built-in support for processing JSON.
41345      */
41346     /**
41347      * @cfg {String} url
41348      * The URL to use for form actions if one isn't supplied in the action options.
41349      */
41350     /**
41351      * @cfg {Boolean} fileUpload
41352      * Set to true if this form is a file upload.
41353      */
41354      
41355     /**
41356      * @cfg {Object} baseParams
41357      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41358      */
41359      /**
41360      
41361     /**
41362      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41363      */
41364     timeout: 30,
41365
41366     // private
41367     activeAction : null,
41368
41369     /**
41370      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41371      * or setValues() data instead of when the form was first created.
41372      */
41373     trackResetOnLoad : false,
41374     
41375     
41376     /**
41377      * childForms - used for multi-tab forms
41378      * @type {Array}
41379      */
41380     childForms : false,
41381     
41382     /**
41383      * allItems - full list of fields.
41384      * @type {Array}
41385      */
41386     allItems : false,
41387     
41388     /**
41389      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41390      * element by passing it or its id or mask the form itself by passing in true.
41391      * @type Mixed
41392      */
41393     waitMsgTarget : false,
41394
41395     // private
41396     initEl : function(el){
41397         this.el = Roo.get(el);
41398         this.id = this.el.id || Roo.id();
41399         this.el.on('submit', this.onSubmit, this);
41400         this.el.addClass('x-form');
41401     },
41402
41403     // private
41404     onSubmit : function(e){
41405         e.stopEvent();
41406     },
41407
41408     /**
41409      * Returns true if client-side validation on the form is successful.
41410      * @return Boolean
41411      */
41412     isValid : function(){
41413         var valid = true;
41414         this.items.each(function(f){
41415            if(!f.validate()){
41416                valid = false;
41417            }
41418         });
41419         return valid;
41420     },
41421
41422     /**
41423      * Returns true if any fields in this form have changed since their original load.
41424      * @return Boolean
41425      */
41426     isDirty : function(){
41427         var dirty = false;
41428         this.items.each(function(f){
41429            if(f.isDirty()){
41430                dirty = true;
41431                return false;
41432            }
41433         });
41434         return dirty;
41435     },
41436
41437     /**
41438      * Performs a predefined action (submit or load) or custom actions you define on this form.
41439      * @param {String} actionName The name of the action type
41440      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41441      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41442      * accept other config options):
41443      * <pre>
41444 Property          Type             Description
41445 ----------------  ---------------  ----------------------------------------------------------------------------------
41446 url               String           The url for the action (defaults to the form's url)
41447 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41448 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41449 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41450                                    validate the form on the client (defaults to false)
41451      * </pre>
41452      * @return {BasicForm} this
41453      */
41454     doAction : function(action, options){
41455         if(typeof action == 'string'){
41456             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41457         }
41458         if(this.fireEvent('beforeaction', this, action) !== false){
41459             this.beforeAction(action);
41460             action.run.defer(100, action);
41461         }
41462         return this;
41463     },
41464
41465     /**
41466      * Shortcut to do a submit action.
41467      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41468      * @return {BasicForm} this
41469      */
41470     submit : function(options){
41471         this.doAction('submit', options);
41472         return this;
41473     },
41474
41475     /**
41476      * Shortcut to do a load action.
41477      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41478      * @return {BasicForm} this
41479      */
41480     load : function(options){
41481         this.doAction('load', options);
41482         return this;
41483     },
41484
41485     /**
41486      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41487      * @param {Record} record The record to edit
41488      * @return {BasicForm} this
41489      */
41490     updateRecord : function(record){
41491         record.beginEdit();
41492         var fs = record.fields;
41493         fs.each(function(f){
41494             var field = this.findField(f.name);
41495             if(field){
41496                 record.set(f.name, field.getValue());
41497             }
41498         }, this);
41499         record.endEdit();
41500         return this;
41501     },
41502
41503     /**
41504      * Loads an Roo.data.Record into this form.
41505      * @param {Record} record The record to load
41506      * @return {BasicForm} this
41507      */
41508     loadRecord : function(record){
41509         this.setValues(record.data);
41510         return this;
41511     },
41512
41513     // private
41514     beforeAction : function(action){
41515         var o = action.options;
41516         
41517        
41518         if(this.waitMsgTarget === true){
41519             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41520         }else if(this.waitMsgTarget){
41521             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41522             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41523         }else {
41524             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41525         }
41526          
41527     },
41528
41529     // private
41530     afterAction : function(action, success){
41531         this.activeAction = null;
41532         var o = action.options;
41533         
41534         if(this.waitMsgTarget === true){
41535             this.el.unmask();
41536         }else if(this.waitMsgTarget){
41537             this.waitMsgTarget.unmask();
41538         }else{
41539             Roo.MessageBox.updateProgress(1);
41540             Roo.MessageBox.hide();
41541         }
41542          
41543         if(success){
41544             if(o.reset){
41545                 this.reset();
41546             }
41547             Roo.callback(o.success, o.scope, [this, action]);
41548             this.fireEvent('actioncomplete', this, action);
41549             
41550         }else{
41551             
41552             // failure condition..
41553             // we have a scenario where updates need confirming.
41554             // eg. if a locking scenario exists..
41555             // we look for { errors : { needs_confirm : true }} in the response.
41556             if (typeof(action.result.errors.needs_confirm) != 'undefined') {
41557                 var _t = this;
41558                 Roo.MessageBox.confirm(
41559                     "Change requires confirmation",
41560                     action.result.errorMsg,
41561                     function(r) {
41562                         if (r != 'yes') {
41563                             return;
41564                         }
41565                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
41566                     }
41567                     
41568                 );
41569                 
41570                 
41571                 
41572                 return;
41573             }
41574             
41575             Roo.callback(o.failure, o.scope, [this, action]);
41576             // show an error message if no failed handler is set..
41577             if (!this.hasListener('actionfailed')) {
41578                 Roo.MessageBox.alert("Error",
41579                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41580                         action.result.errorMsg :
41581                         "Saving Failed, please check your entries"
41582                 );
41583             }
41584             
41585             this.fireEvent('actionfailed', this, action);
41586         }
41587         
41588     },
41589
41590     /**
41591      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41592      * @param {String} id The value to search for
41593      * @return Field
41594      */
41595     findField : function(id){
41596         var field = this.items.get(id);
41597         if(!field){
41598             this.items.each(function(f){
41599                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41600                     field = f;
41601                     return false;
41602                 }
41603             });
41604         }
41605         return field || null;
41606     },
41607
41608     /**
41609      * Add a secondary form to this one, 
41610      * Used to provide tabbed forms. One form is primary, with hidden values 
41611      * which mirror the elements from the other forms.
41612      * 
41613      * @param {Roo.form.Form} form to add.
41614      * 
41615      */
41616     addForm : function(form)
41617     {
41618        
41619         if (this.childForms.indexOf(form) > -1) {
41620             // already added..
41621             return;
41622         }
41623         this.childForms.push(form);
41624         var n = '';
41625         Roo.each(form.allItems, function (fe) {
41626             
41627             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41628             if (this.findField(n)) { // already added..
41629                 return;
41630             }
41631             var add = new Roo.form.Hidden({
41632                 name : n
41633             });
41634             add.render(this.el);
41635             
41636             this.add( add );
41637         }, this);
41638         
41639     },
41640     /**
41641      * Mark fields in this form invalid in bulk.
41642      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41643      * @return {BasicForm} this
41644      */
41645     markInvalid : function(errors){
41646         if(errors instanceof Array){
41647             for(var i = 0, len = errors.length; i < len; i++){
41648                 var fieldError = errors[i];
41649                 var f = this.findField(fieldError.id);
41650                 if(f){
41651                     f.markInvalid(fieldError.msg);
41652                 }
41653             }
41654         }else{
41655             var field, id;
41656             for(id in errors){
41657                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41658                     field.markInvalid(errors[id]);
41659                 }
41660             }
41661         }
41662         Roo.each(this.childForms || [], function (f) {
41663             f.markInvalid(errors);
41664         });
41665         
41666         return this;
41667     },
41668
41669     /**
41670      * Set values for fields in this form in bulk.
41671      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41672      * @return {BasicForm} this
41673      */
41674     setValues : function(values){
41675         if(values instanceof Array){ // array of objects
41676             for(var i = 0, len = values.length; i < len; i++){
41677                 var v = values[i];
41678                 var f = this.findField(v.id);
41679                 if(f){
41680                     f.setValue(v.value);
41681                     if(this.trackResetOnLoad){
41682                         f.originalValue = f.getValue();
41683                     }
41684                 }
41685             }
41686         }else{ // object hash
41687             var field, id;
41688             for(id in values){
41689                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41690                     
41691                     if (field.setFromData && 
41692                         field.valueField && 
41693                         field.displayField &&
41694                         // combos' with local stores can 
41695                         // be queried via setValue()
41696                         // to set their value..
41697                         (field.store && !field.store.isLocal)
41698                         ) {
41699                         // it's a combo
41700                         var sd = { };
41701                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41702                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41703                         field.setFromData(sd);
41704                         
41705                     } else {
41706                         field.setValue(values[id]);
41707                     }
41708                     
41709                     
41710                     if(this.trackResetOnLoad){
41711                         field.originalValue = field.getValue();
41712                     }
41713                 }
41714             }
41715         }
41716          
41717         Roo.each(this.childForms || [], function (f) {
41718             f.setValues(values);
41719         });
41720                 
41721         return this;
41722     },
41723
41724     /**
41725      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41726      * they are returned as an array.
41727      * @param {Boolean} asString
41728      * @return {Object}
41729      */
41730     getValues : function(asString){
41731         if (this.childForms) {
41732             // copy values from the child forms
41733             Roo.each(this.childForms, function (f) {
41734                 this.setValues(f.getValues());
41735             }, this);
41736         }
41737         
41738         
41739         
41740         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41741         if(asString === true){
41742             return fs;
41743         }
41744         return Roo.urlDecode(fs);
41745     },
41746     
41747     /**
41748      * Returns the fields in this form as an object with key/value pairs. 
41749      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41750      * @return {Object}
41751      */
41752     getFieldValues : function(with_hidden)
41753     {
41754         if (this.childForms) {
41755             // copy values from the child forms
41756             // should this call getFieldValues - probably not as we do not currently copy
41757             // hidden fields when we generate..
41758             Roo.each(this.childForms, function (f) {
41759                 this.setValues(f.getValues());
41760             }, this);
41761         }
41762         
41763         var ret = {};
41764         this.items.each(function(f){
41765             if (!f.getName()) {
41766                 return;
41767             }
41768             var v = f.getValue();
41769             // not sure if this supported any more..
41770             if ((typeof(v) == 'object') && f.getRawValue) {
41771                 v = f.getRawValue() ; // dates..
41772             }
41773             // combo boxes where name != hiddenName...
41774             if (f.name != f.getName()) {
41775                 ret[f.name] = f.getRawValue();
41776             }
41777             ret[f.getName()] = v;
41778         });
41779         
41780         return ret;
41781     },
41782
41783     /**
41784      * Clears all invalid messages in this form.
41785      * @return {BasicForm} this
41786      */
41787     clearInvalid : function(){
41788         this.items.each(function(f){
41789            f.clearInvalid();
41790         });
41791         
41792         Roo.each(this.childForms || [], function (f) {
41793             f.clearInvalid();
41794         });
41795         
41796         
41797         return this;
41798     },
41799
41800     /**
41801      * Resets this form.
41802      * @return {BasicForm} this
41803      */
41804     reset : function(){
41805         this.items.each(function(f){
41806             f.reset();
41807         });
41808         
41809         Roo.each(this.childForms || [], function (f) {
41810             f.reset();
41811         });
41812        
41813         
41814         return this;
41815     },
41816
41817     /**
41818      * Add Roo.form components to this form.
41819      * @param {Field} field1
41820      * @param {Field} field2 (optional)
41821      * @param {Field} etc (optional)
41822      * @return {BasicForm} this
41823      */
41824     add : function(){
41825         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41826         return this;
41827     },
41828
41829
41830     /**
41831      * Removes a field from the items collection (does NOT remove its markup).
41832      * @param {Field} field
41833      * @return {BasicForm} this
41834      */
41835     remove : function(field){
41836         this.items.remove(field);
41837         return this;
41838     },
41839
41840     /**
41841      * Looks at the fields in this form, checks them for an id attribute,
41842      * and calls applyTo on the existing dom element with that id.
41843      * @return {BasicForm} this
41844      */
41845     render : function(){
41846         this.items.each(function(f){
41847             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41848                 f.applyTo(f.id);
41849             }
41850         });
41851         return this;
41852     },
41853
41854     /**
41855      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41856      * @param {Object} values
41857      * @return {BasicForm} this
41858      */
41859     applyToFields : function(o){
41860         this.items.each(function(f){
41861            Roo.apply(f, o);
41862         });
41863         return this;
41864     },
41865
41866     /**
41867      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41868      * @param {Object} values
41869      * @return {BasicForm} this
41870      */
41871     applyIfToFields : function(o){
41872         this.items.each(function(f){
41873            Roo.applyIf(f, o);
41874         });
41875         return this;
41876     }
41877 });
41878
41879 // back compat
41880 Roo.BasicForm = Roo.form.BasicForm;/*
41881  * Based on:
41882  * Ext JS Library 1.1.1
41883  * Copyright(c) 2006-2007, Ext JS, LLC.
41884  *
41885  * Originally Released Under LGPL - original licence link has changed is not relivant.
41886  *
41887  * Fork - LGPL
41888  * <script type="text/javascript">
41889  */
41890
41891 /**
41892  * @class Roo.form.Form
41893  * @extends Roo.form.BasicForm
41894  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41895  * @constructor
41896  * @param {Object} config Configuration options
41897  */
41898 Roo.form.Form = function(config){
41899     var xitems =  [];
41900     if (config.items) {
41901         xitems = config.items;
41902         delete config.items;
41903     }
41904    
41905     
41906     Roo.form.Form.superclass.constructor.call(this, null, config);
41907     this.url = this.url || this.action;
41908     if(!this.root){
41909         this.root = new Roo.form.Layout(Roo.applyIf({
41910             id: Roo.id()
41911         }, config));
41912     }
41913     this.active = this.root;
41914     /**
41915      * Array of all the buttons that have been added to this form via {@link addButton}
41916      * @type Array
41917      */
41918     this.buttons = [];
41919     this.allItems = [];
41920     this.addEvents({
41921         /**
41922          * @event clientvalidation
41923          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41924          * @param {Form} this
41925          * @param {Boolean} valid true if the form has passed client-side validation
41926          */
41927         clientvalidation: true,
41928         /**
41929          * @event rendered
41930          * Fires when the form is rendered
41931          * @param {Roo.form.Form} form
41932          */
41933         rendered : true
41934     });
41935     
41936     if (this.progressUrl) {
41937             // push a hidden field onto the list of fields..
41938             this.addxtype( {
41939                     xns: Roo.form, 
41940                     xtype : 'Hidden', 
41941                     name : 'UPLOAD_IDENTIFIER' 
41942             });
41943         }
41944         
41945     
41946     Roo.each(xitems, this.addxtype, this);
41947     
41948     
41949     
41950 };
41951
41952 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41953     /**
41954      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41955      */
41956     /**
41957      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41958      */
41959     /**
41960      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41961      */
41962     buttonAlign:'center',
41963
41964     /**
41965      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41966      */
41967     minButtonWidth:75,
41968
41969     /**
41970      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41971      * This property cascades to child containers if not set.
41972      */
41973     labelAlign:'left',
41974
41975     /**
41976      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41977      * fires a looping event with that state. This is required to bind buttons to the valid
41978      * state using the config value formBind:true on the button.
41979      */
41980     monitorValid : false,
41981
41982     /**
41983      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41984      */
41985     monitorPoll : 200,
41986     
41987     /**
41988      * @cfg {String} progressUrl - Url to return progress data 
41989      */
41990     
41991     progressUrl : false,
41992   
41993     /**
41994      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41995      * fields are added and the column is closed. If no fields are passed the column remains open
41996      * until end() is called.
41997      * @param {Object} config The config to pass to the column
41998      * @param {Field} field1 (optional)
41999      * @param {Field} field2 (optional)
42000      * @param {Field} etc (optional)
42001      * @return Column The column container object
42002      */
42003     column : function(c){
42004         var col = new Roo.form.Column(c);
42005         this.start(col);
42006         if(arguments.length > 1){ // duplicate code required because of Opera
42007             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42008             this.end();
42009         }
42010         return col;
42011     },
42012
42013     /**
42014      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
42015      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
42016      * until end() is called.
42017      * @param {Object} config The config to pass to the fieldset
42018      * @param {Field} field1 (optional)
42019      * @param {Field} field2 (optional)
42020      * @param {Field} etc (optional)
42021      * @return FieldSet The fieldset container object
42022      */
42023     fieldset : function(c){
42024         var fs = new Roo.form.FieldSet(c);
42025         this.start(fs);
42026         if(arguments.length > 1){ // duplicate code required because of Opera
42027             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42028             this.end();
42029         }
42030         return fs;
42031     },
42032
42033     /**
42034      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
42035      * fields are added and the container is closed. If no fields are passed the container remains open
42036      * until end() is called.
42037      * @param {Object} config The config to pass to the Layout
42038      * @param {Field} field1 (optional)
42039      * @param {Field} field2 (optional)
42040      * @param {Field} etc (optional)
42041      * @return Layout The container object
42042      */
42043     container : function(c){
42044         var l = new Roo.form.Layout(c);
42045         this.start(l);
42046         if(arguments.length > 1){ // duplicate code required because of Opera
42047             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42048             this.end();
42049         }
42050         return l;
42051     },
42052
42053     /**
42054      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
42055      * @param {Object} container A Roo.form.Layout or subclass of Layout
42056      * @return {Form} this
42057      */
42058     start : function(c){
42059         // cascade label info
42060         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
42061         this.active.stack.push(c);
42062         c.ownerCt = this.active;
42063         this.active = c;
42064         return this;
42065     },
42066
42067     /**
42068      * Closes the current open container
42069      * @return {Form} this
42070      */
42071     end : function(){
42072         if(this.active == this.root){
42073             return this;
42074         }
42075         this.active = this.active.ownerCt;
42076         return this;
42077     },
42078
42079     /**
42080      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
42081      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
42082      * as the label of the field.
42083      * @param {Field} field1
42084      * @param {Field} field2 (optional)
42085      * @param {Field} etc. (optional)
42086      * @return {Form} this
42087      */
42088     add : function(){
42089         this.active.stack.push.apply(this.active.stack, arguments);
42090         this.allItems.push.apply(this.allItems,arguments);
42091         var r = [];
42092         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
42093             if(a[i].isFormField){
42094                 r.push(a[i]);
42095             }
42096         }
42097         if(r.length > 0){
42098             Roo.form.Form.superclass.add.apply(this, r);
42099         }
42100         return this;
42101     },
42102     
42103
42104     
42105     
42106     
42107      /**
42108      * Find any element that has been added to a form, using it's ID or name
42109      * This can include framesets, columns etc. along with regular fields..
42110      * @param {String} id - id or name to find.
42111      
42112      * @return {Element} e - or false if nothing found.
42113      */
42114     findbyId : function(id)
42115     {
42116         var ret = false;
42117         if (!id) {
42118             return ret;
42119         }
42120         Roo.each(this.allItems, function(f){
42121             if (f.id == id || f.name == id ){
42122                 ret = f;
42123                 return false;
42124             }
42125         });
42126         return ret;
42127     },
42128
42129     
42130     
42131     /**
42132      * Render this form into the passed container. This should only be called once!
42133      * @param {String/HTMLElement/Element} container The element this component should be rendered into
42134      * @return {Form} this
42135      */
42136     render : function(ct)
42137     {
42138         
42139         
42140         
42141         ct = Roo.get(ct);
42142         var o = this.autoCreate || {
42143             tag: 'form',
42144             method : this.method || 'POST',
42145             id : this.id || Roo.id()
42146         };
42147         this.initEl(ct.createChild(o));
42148
42149         this.root.render(this.el);
42150         
42151        
42152              
42153         this.items.each(function(f){
42154             f.render('x-form-el-'+f.id);
42155         });
42156
42157         if(this.buttons.length > 0){
42158             // tables are required to maintain order and for correct IE layout
42159             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
42160                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
42161                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
42162             }}, null, true);
42163             var tr = tb.getElementsByTagName('tr')[0];
42164             for(var i = 0, len = this.buttons.length; i < len; i++) {
42165                 var b = this.buttons[i];
42166                 var td = document.createElement('td');
42167                 td.className = 'x-form-btn-td';
42168                 b.render(tr.appendChild(td));
42169             }
42170         }
42171         if(this.monitorValid){ // initialize after render
42172             this.startMonitoring();
42173         }
42174         this.fireEvent('rendered', this);
42175         return this;
42176     },
42177
42178     /**
42179      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42180      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42181      * object or a valid Roo.DomHelper element config
42182      * @param {Function} handler The function called when the button is clicked
42183      * @param {Object} scope (optional) The scope of the handler function
42184      * @return {Roo.Button}
42185      */
42186     addButton : function(config, handler, scope){
42187         var bc = {
42188             handler: handler,
42189             scope: scope,
42190             minWidth: this.minButtonWidth,
42191             hideParent:true
42192         };
42193         if(typeof config == "string"){
42194             bc.text = config;
42195         }else{
42196             Roo.apply(bc, config);
42197         }
42198         var btn = new Roo.Button(null, bc);
42199         this.buttons.push(btn);
42200         return btn;
42201     },
42202
42203      /**
42204      * Adds a series of form elements (using the xtype property as the factory method.
42205      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42206      * @param {Object} config 
42207      */
42208     
42209     addxtype : function()
42210     {
42211         var ar = Array.prototype.slice.call(arguments, 0);
42212         var ret = false;
42213         for(var i = 0; i < ar.length; i++) {
42214             if (!ar[i]) {
42215                 continue; // skip -- if this happends something invalid got sent, we 
42216                 // should ignore it, as basically that interface element will not show up
42217                 // and that should be pretty obvious!!
42218             }
42219             
42220             if (Roo.form[ar[i].xtype]) {
42221                 ar[i].form = this;
42222                 var fe = Roo.factory(ar[i], Roo.form);
42223                 if (!ret) {
42224                     ret = fe;
42225                 }
42226                 fe.form = this;
42227                 if (fe.store) {
42228                     fe.store.form = this;
42229                 }
42230                 if (fe.isLayout) {  
42231                          
42232                     this.start(fe);
42233                     this.allItems.push(fe);
42234                     if (fe.items && fe.addxtype) {
42235                         fe.addxtype.apply(fe, fe.items);
42236                         delete fe.items;
42237                     }
42238                      this.end();
42239                     continue;
42240                 }
42241                 
42242                 
42243                  
42244                 this.add(fe);
42245               //  console.log('adding ' + ar[i].xtype);
42246             }
42247             if (ar[i].xtype == 'Button') {  
42248                 //console.log('adding button');
42249                 //console.log(ar[i]);
42250                 this.addButton(ar[i]);
42251                 this.allItems.push(fe);
42252                 continue;
42253             }
42254             
42255             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42256                 alert('end is not supported on xtype any more, use items');
42257             //    this.end();
42258             //    //console.log('adding end');
42259             }
42260             
42261         }
42262         return ret;
42263     },
42264     
42265     /**
42266      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42267      * option "monitorValid"
42268      */
42269     startMonitoring : function(){
42270         if(!this.bound){
42271             this.bound = true;
42272             Roo.TaskMgr.start({
42273                 run : this.bindHandler,
42274                 interval : this.monitorPoll || 200,
42275                 scope: this
42276             });
42277         }
42278     },
42279
42280     /**
42281      * Stops monitoring of the valid state of this form
42282      */
42283     stopMonitoring : function(){
42284         this.bound = false;
42285     },
42286
42287     // private
42288     bindHandler : function(){
42289         if(!this.bound){
42290             return false; // stops binding
42291         }
42292         var valid = true;
42293         this.items.each(function(f){
42294             if(!f.isValid(true)){
42295                 valid = false;
42296                 return false;
42297             }
42298         });
42299         for(var i = 0, len = this.buttons.length; i < len; i++){
42300             var btn = this.buttons[i];
42301             if(btn.formBind === true && btn.disabled === valid){
42302                 btn.setDisabled(!valid);
42303             }
42304         }
42305         this.fireEvent('clientvalidation', this, valid);
42306     }
42307     
42308     
42309     
42310     
42311     
42312     
42313     
42314     
42315 });
42316
42317
42318 // back compat
42319 Roo.Form = Roo.form.Form;
42320 /*
42321  * Based on:
42322  * Ext JS Library 1.1.1
42323  * Copyright(c) 2006-2007, Ext JS, LLC.
42324  *
42325  * Originally Released Under LGPL - original licence link has changed is not relivant.
42326  *
42327  * Fork - LGPL
42328  * <script type="text/javascript">
42329  */
42330  
42331  /**
42332  * @class Roo.form.Action
42333  * Internal Class used to handle form actions
42334  * @constructor
42335  * @param {Roo.form.BasicForm} el The form element or its id
42336  * @param {Object} config Configuration options
42337  */
42338  
42339  
42340 // define the action interface
42341 Roo.form.Action = function(form, options){
42342     this.form = form;
42343     this.options = options || {};
42344 };
42345 /**
42346  * Client Validation Failed
42347  * @const 
42348  */
42349 Roo.form.Action.CLIENT_INVALID = 'client';
42350 /**
42351  * Server Validation Failed
42352  * @const 
42353  */
42354  Roo.form.Action.SERVER_INVALID = 'server';
42355  /**
42356  * Connect to Server Failed
42357  * @const 
42358  */
42359 Roo.form.Action.CONNECT_FAILURE = 'connect';
42360 /**
42361  * Reading Data from Server Failed
42362  * @const 
42363  */
42364 Roo.form.Action.LOAD_FAILURE = 'load';
42365
42366 Roo.form.Action.prototype = {
42367     type : 'default',
42368     failureType : undefined,
42369     response : undefined,
42370     result : undefined,
42371
42372     // interface method
42373     run : function(options){
42374
42375     },
42376
42377     // interface method
42378     success : function(response){
42379
42380     },
42381
42382     // interface method
42383     handleResponse : function(response){
42384
42385     },
42386
42387     // default connection failure
42388     failure : function(response){
42389         
42390         this.response = response;
42391         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42392         this.form.afterAction(this, false);
42393     },
42394
42395     processResponse : function(response){
42396         this.response = response;
42397         if(!response.responseText){
42398             return true;
42399         }
42400         this.result = this.handleResponse(response);
42401         return this.result;
42402     },
42403
42404     // utility functions used internally
42405     getUrl : function(appendParams){
42406         var url = this.options.url || this.form.url || this.form.el.dom.action;
42407         if(appendParams){
42408             var p = this.getParams();
42409             if(p){
42410                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42411             }
42412         }
42413         return url;
42414     },
42415
42416     getMethod : function(){
42417         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42418     },
42419
42420     getParams : function(){
42421         var bp = this.form.baseParams;
42422         var p = this.options.params;
42423         if(p){
42424             if(typeof p == "object"){
42425                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42426             }else if(typeof p == 'string' && bp){
42427                 p += '&' + Roo.urlEncode(bp);
42428             }
42429         }else if(bp){
42430             p = Roo.urlEncode(bp);
42431         }
42432         return p;
42433     },
42434
42435     createCallback : function(){
42436         return {
42437             success: this.success,
42438             failure: this.failure,
42439             scope: this,
42440             timeout: (this.form.timeout*1000),
42441             upload: this.form.fileUpload ? this.success : undefined
42442         };
42443     }
42444 };
42445
42446 Roo.form.Action.Submit = function(form, options){
42447     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42448 };
42449
42450 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42451     type : 'submit',
42452
42453     haveProgress : false,
42454     uploadComplete : false,
42455     
42456     // uploadProgress indicator.
42457     uploadProgress : function()
42458     {
42459         if (!this.form.progressUrl) {
42460             return;
42461         }
42462         
42463         if (!this.haveProgress) {
42464             Roo.MessageBox.progress("Uploading", "Uploading");
42465         }
42466         if (this.uploadComplete) {
42467            Roo.MessageBox.hide();
42468            return;
42469         }
42470         
42471         this.haveProgress = true;
42472    
42473         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42474         
42475         var c = new Roo.data.Connection();
42476         c.request({
42477             url : this.form.progressUrl,
42478             params: {
42479                 id : uid
42480             },
42481             method: 'GET',
42482             success : function(req){
42483                //console.log(data);
42484                 var rdata = false;
42485                 var edata;
42486                 try  {
42487                    rdata = Roo.decode(req.responseText)
42488                 } catch (e) {
42489                     Roo.log("Invalid data from server..");
42490                     Roo.log(edata);
42491                     return;
42492                 }
42493                 if (!rdata || !rdata.success) {
42494                     Roo.log(rdata);
42495                     return;
42496                 }
42497                 var data = rdata.data;
42498                 
42499                 if (this.uploadComplete) {
42500                    Roo.MessageBox.hide();
42501                    return;
42502                 }
42503                    
42504                 if (data){
42505                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42506                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42507                     );
42508                 }
42509                 this.uploadProgress.defer(2000,this);
42510             },
42511        
42512             failure: function(data) {
42513                 Roo.log('progress url failed ');
42514                 Roo.log(data);
42515             },
42516             scope : this
42517         });
42518            
42519     },
42520     
42521     
42522     run : function()
42523     {
42524         // run get Values on the form, so it syncs any secondary forms.
42525         this.form.getValues();
42526         
42527         var o = this.options;
42528         var method = this.getMethod();
42529         var isPost = method == 'POST';
42530         if(o.clientValidation === false || this.form.isValid()){
42531             
42532             if (this.form.progressUrl) {
42533                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42534                     (new Date() * 1) + '' + Math.random());
42535                     
42536             } 
42537             
42538             
42539             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42540                 form:this.form.el.dom,
42541                 url:this.getUrl(!isPost),
42542                 method: method,
42543                 params:isPost ? this.getParams() : null,
42544                 isUpload: this.form.fileUpload
42545             }));
42546             
42547             this.uploadProgress();
42548
42549         }else if (o.clientValidation !== false){ // client validation failed
42550             this.failureType = Roo.form.Action.CLIENT_INVALID;
42551             this.form.afterAction(this, false);
42552         }
42553     },
42554
42555     success : function(response)
42556     {
42557         this.uploadComplete= true;
42558         if (this.haveProgress) {
42559             Roo.MessageBox.hide();
42560         }
42561         
42562         
42563         var result = this.processResponse(response);
42564         if(result === true || result.success){
42565             this.form.afterAction(this, true);
42566             return;
42567         }
42568         if(result.errors){
42569             this.form.markInvalid(result.errors);
42570             this.failureType = Roo.form.Action.SERVER_INVALID;
42571         }
42572         this.form.afterAction(this, false);
42573     },
42574     failure : function(response)
42575     {
42576         this.uploadComplete= true;
42577         if (this.haveProgress) {
42578             Roo.MessageBox.hide();
42579         }
42580         
42581         this.response = response;
42582         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42583         this.form.afterAction(this, false);
42584     },
42585     
42586     handleResponse : function(response){
42587         if(this.form.errorReader){
42588             var rs = this.form.errorReader.read(response);
42589             var errors = [];
42590             if(rs.records){
42591                 for(var i = 0, len = rs.records.length; i < len; i++) {
42592                     var r = rs.records[i];
42593                     errors[i] = r.data;
42594                 }
42595             }
42596             if(errors.length < 1){
42597                 errors = null;
42598             }
42599             return {
42600                 success : rs.success,
42601                 errors : errors
42602             };
42603         }
42604         var ret = false;
42605         try {
42606             ret = Roo.decode(response.responseText);
42607         } catch (e) {
42608             ret = {
42609                 success: false,
42610                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42611                 errors : []
42612             };
42613         }
42614         return ret;
42615         
42616     }
42617 });
42618
42619
42620 Roo.form.Action.Load = function(form, options){
42621     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42622     this.reader = this.form.reader;
42623 };
42624
42625 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42626     type : 'load',
42627
42628     run : function(){
42629         
42630         Roo.Ajax.request(Roo.apply(
42631                 this.createCallback(), {
42632                     method:this.getMethod(),
42633                     url:this.getUrl(false),
42634                     params:this.getParams()
42635         }));
42636     },
42637
42638     success : function(response){
42639         
42640         var result = this.processResponse(response);
42641         if(result === true || !result.success || !result.data){
42642             this.failureType = Roo.form.Action.LOAD_FAILURE;
42643             this.form.afterAction(this, false);
42644             return;
42645         }
42646         this.form.clearInvalid();
42647         this.form.setValues(result.data);
42648         this.form.afterAction(this, true);
42649     },
42650
42651     handleResponse : function(response){
42652         if(this.form.reader){
42653             var rs = this.form.reader.read(response);
42654             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42655             return {
42656                 success : rs.success,
42657                 data : data
42658             };
42659         }
42660         return Roo.decode(response.responseText);
42661     }
42662 });
42663
42664 Roo.form.Action.ACTION_TYPES = {
42665     'load' : Roo.form.Action.Load,
42666     'submit' : Roo.form.Action.Submit
42667 };/*
42668  * Based on:
42669  * Ext JS Library 1.1.1
42670  * Copyright(c) 2006-2007, Ext JS, LLC.
42671  *
42672  * Originally Released Under LGPL - original licence link has changed is not relivant.
42673  *
42674  * Fork - LGPL
42675  * <script type="text/javascript">
42676  */
42677  
42678 /**
42679  * @class Roo.form.Layout
42680  * @extends Roo.Component
42681  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42682  * @constructor
42683  * @param {Object} config Configuration options
42684  */
42685 Roo.form.Layout = function(config){
42686     var xitems = [];
42687     if (config.items) {
42688         xitems = config.items;
42689         delete config.items;
42690     }
42691     Roo.form.Layout.superclass.constructor.call(this, config);
42692     this.stack = [];
42693     Roo.each(xitems, this.addxtype, this);
42694      
42695 };
42696
42697 Roo.extend(Roo.form.Layout, Roo.Component, {
42698     /**
42699      * @cfg {String/Object} autoCreate
42700      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42701      */
42702     /**
42703      * @cfg {String/Object/Function} style
42704      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42705      * a function which returns such a specification.
42706      */
42707     /**
42708      * @cfg {String} labelAlign
42709      * Valid values are "left," "top" and "right" (defaults to "left")
42710      */
42711     /**
42712      * @cfg {Number} labelWidth
42713      * Fixed width in pixels of all field labels (defaults to undefined)
42714      */
42715     /**
42716      * @cfg {Boolean} clear
42717      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42718      */
42719     clear : true,
42720     /**
42721      * @cfg {String} labelSeparator
42722      * The separator to use after field labels (defaults to ':')
42723      */
42724     labelSeparator : ':',
42725     /**
42726      * @cfg {Boolean} hideLabels
42727      * True to suppress the display of field labels in this layout (defaults to false)
42728      */
42729     hideLabels : false,
42730
42731     // private
42732     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42733     
42734     isLayout : true,
42735     
42736     // private
42737     onRender : function(ct, position){
42738         if(this.el){ // from markup
42739             this.el = Roo.get(this.el);
42740         }else {  // generate
42741             var cfg = this.getAutoCreate();
42742             this.el = ct.createChild(cfg, position);
42743         }
42744         if(this.style){
42745             this.el.applyStyles(this.style);
42746         }
42747         if(this.labelAlign){
42748             this.el.addClass('x-form-label-'+this.labelAlign);
42749         }
42750         if(this.hideLabels){
42751             this.labelStyle = "display:none";
42752             this.elementStyle = "padding-left:0;";
42753         }else{
42754             if(typeof this.labelWidth == 'number'){
42755                 this.labelStyle = "width:"+this.labelWidth+"px;";
42756                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42757             }
42758             if(this.labelAlign == 'top'){
42759                 this.labelStyle = "width:auto;";
42760                 this.elementStyle = "padding-left:0;";
42761             }
42762         }
42763         var stack = this.stack;
42764         var slen = stack.length;
42765         if(slen > 0){
42766             if(!this.fieldTpl){
42767                 var t = new Roo.Template(
42768                     '<div class="x-form-item {5}">',
42769                         '<label for="{0}" style="{2}">{1}{4}</label>',
42770                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42771                         '</div>',
42772                     '</div><div class="x-form-clear-left"></div>'
42773                 );
42774                 t.disableFormats = true;
42775                 t.compile();
42776                 Roo.form.Layout.prototype.fieldTpl = t;
42777             }
42778             for(var i = 0; i < slen; i++) {
42779                 if(stack[i].isFormField){
42780                     this.renderField(stack[i]);
42781                 }else{
42782                     this.renderComponent(stack[i]);
42783                 }
42784             }
42785         }
42786         if(this.clear){
42787             this.el.createChild({cls:'x-form-clear'});
42788         }
42789     },
42790
42791     // private
42792     renderField : function(f){
42793         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42794                f.id, //0
42795                f.fieldLabel, //1
42796                f.labelStyle||this.labelStyle||'', //2
42797                this.elementStyle||'', //3
42798                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42799                f.itemCls||this.itemCls||''  //5
42800        ], true).getPrevSibling());
42801     },
42802
42803     // private
42804     renderComponent : function(c){
42805         c.render(c.isLayout ? this.el : this.el.createChild());    
42806     },
42807     /**
42808      * Adds a object form elements (using the xtype property as the factory method.)
42809      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42810      * @param {Object} config 
42811      */
42812     addxtype : function(o)
42813     {
42814         // create the lement.
42815         o.form = this.form;
42816         var fe = Roo.factory(o, Roo.form);
42817         this.form.allItems.push(fe);
42818         this.stack.push(fe);
42819         
42820         if (fe.isFormField) {
42821             this.form.items.add(fe);
42822         }
42823          
42824         return fe;
42825     }
42826 });
42827
42828 /**
42829  * @class Roo.form.Column
42830  * @extends Roo.form.Layout
42831  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42832  * @constructor
42833  * @param {Object} config Configuration options
42834  */
42835 Roo.form.Column = function(config){
42836     Roo.form.Column.superclass.constructor.call(this, config);
42837 };
42838
42839 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42840     /**
42841      * @cfg {Number/String} width
42842      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42843      */
42844     /**
42845      * @cfg {String/Object} autoCreate
42846      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42847      */
42848
42849     // private
42850     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42851
42852     // private
42853     onRender : function(ct, position){
42854         Roo.form.Column.superclass.onRender.call(this, ct, position);
42855         if(this.width){
42856             this.el.setWidth(this.width);
42857         }
42858     }
42859 });
42860
42861
42862 /**
42863  * @class Roo.form.Row
42864  * @extends Roo.form.Layout
42865  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42866  * @constructor
42867  * @param {Object} config Configuration options
42868  */
42869
42870  
42871 Roo.form.Row = function(config){
42872     Roo.form.Row.superclass.constructor.call(this, config);
42873 };
42874  
42875 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42876       /**
42877      * @cfg {Number/String} width
42878      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42879      */
42880     /**
42881      * @cfg {Number/String} height
42882      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42883      */
42884     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42885     
42886     padWidth : 20,
42887     // private
42888     onRender : function(ct, position){
42889         //console.log('row render');
42890         if(!this.rowTpl){
42891             var t = new Roo.Template(
42892                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42893                     '<label for="{0}" style="{2}">{1}{4}</label>',
42894                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42895                     '</div>',
42896                 '</div>'
42897             );
42898             t.disableFormats = true;
42899             t.compile();
42900             Roo.form.Layout.prototype.rowTpl = t;
42901         }
42902         this.fieldTpl = this.rowTpl;
42903         
42904         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42905         var labelWidth = 100;
42906         
42907         if ((this.labelAlign != 'top')) {
42908             if (typeof this.labelWidth == 'number') {
42909                 labelWidth = this.labelWidth
42910             }
42911             this.padWidth =  20 + labelWidth;
42912             
42913         }
42914         
42915         Roo.form.Column.superclass.onRender.call(this, ct, position);
42916         if(this.width){
42917             this.el.setWidth(this.width);
42918         }
42919         if(this.height){
42920             this.el.setHeight(this.height);
42921         }
42922     },
42923     
42924     // private
42925     renderField : function(f){
42926         f.fieldEl = this.fieldTpl.append(this.el, [
42927                f.id, f.fieldLabel,
42928                f.labelStyle||this.labelStyle||'',
42929                this.elementStyle||'',
42930                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42931                f.itemCls||this.itemCls||'',
42932                f.width ? f.width + this.padWidth : 160 + this.padWidth
42933        ],true);
42934     }
42935 });
42936  
42937
42938 /**
42939  * @class Roo.form.FieldSet
42940  * @extends Roo.form.Layout
42941  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42942  * @constructor
42943  * @param {Object} config Configuration options
42944  */
42945 Roo.form.FieldSet = function(config){
42946     Roo.form.FieldSet.superclass.constructor.call(this, config);
42947 };
42948
42949 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42950     /**
42951      * @cfg {String} legend
42952      * The text to display as the legend for the FieldSet (defaults to '')
42953      */
42954     /**
42955      * @cfg {String/Object} autoCreate
42956      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42957      */
42958
42959     // private
42960     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42961
42962     // private
42963     onRender : function(ct, position){
42964         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42965         if(this.legend){
42966             this.setLegend(this.legend);
42967         }
42968     },
42969
42970     // private
42971     setLegend : function(text){
42972         if(this.rendered){
42973             this.el.child('legend').update(text);
42974         }
42975     }
42976 });/*
42977  * Based on:
42978  * Ext JS Library 1.1.1
42979  * Copyright(c) 2006-2007, Ext JS, LLC.
42980  *
42981  * Originally Released Under LGPL - original licence link has changed is not relivant.
42982  *
42983  * Fork - LGPL
42984  * <script type="text/javascript">
42985  */
42986 /**
42987  * @class Roo.form.VTypes
42988  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42989  * @singleton
42990  */
42991 Roo.form.VTypes = function(){
42992     // closure these in so they are only created once.
42993     var alpha = /^[a-zA-Z_]+$/;
42994     var alphanum = /^[a-zA-Z0-9_]+$/;
42995     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42996     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42997
42998     // All these messages and functions are configurable
42999     return {
43000         /**
43001          * The function used to validate email addresses
43002          * @param {String} value The email address
43003          */
43004         'email' : function(v){
43005             return email.test(v);
43006         },
43007         /**
43008          * The error text to display when the email validation function returns false
43009          * @type String
43010          */
43011         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
43012         /**
43013          * The keystroke filter mask to be applied on email input
43014          * @type RegExp
43015          */
43016         'emailMask' : /[a-z0-9_\.\-@]/i,
43017
43018         /**
43019          * The function used to validate URLs
43020          * @param {String} value The URL
43021          */
43022         'url' : function(v){
43023             return url.test(v);
43024         },
43025         /**
43026          * The error text to display when the url validation function returns false
43027          * @type String
43028          */
43029         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
43030         
43031         /**
43032          * The function used to validate alpha values
43033          * @param {String} value The value
43034          */
43035         'alpha' : function(v){
43036             return alpha.test(v);
43037         },
43038         /**
43039          * The error text to display when the alpha validation function returns false
43040          * @type String
43041          */
43042         'alphaText' : 'This field should only contain letters and _',
43043         /**
43044          * The keystroke filter mask to be applied on alpha input
43045          * @type RegExp
43046          */
43047         'alphaMask' : /[a-z_]/i,
43048
43049         /**
43050          * The function used to validate alphanumeric values
43051          * @param {String} value The value
43052          */
43053         'alphanum' : function(v){
43054             return alphanum.test(v);
43055         },
43056         /**
43057          * The error text to display when the alphanumeric validation function returns false
43058          * @type String
43059          */
43060         'alphanumText' : 'This field should only contain letters, numbers and _',
43061         /**
43062          * The keystroke filter mask to be applied on alphanumeric input
43063          * @type RegExp
43064          */
43065         'alphanumMask' : /[a-z0-9_]/i
43066     };
43067 }();//<script type="text/javascript">
43068
43069 /**
43070  * @class Roo.form.FCKeditor
43071  * @extends Roo.form.TextArea
43072  * Wrapper around the FCKEditor http://www.fckeditor.net
43073  * @constructor
43074  * Creates a new FCKeditor
43075  * @param {Object} config Configuration options
43076  */
43077 Roo.form.FCKeditor = function(config){
43078     Roo.form.FCKeditor.superclass.constructor.call(this, config);
43079     this.addEvents({
43080          /**
43081          * @event editorinit
43082          * Fired when the editor is initialized - you can add extra handlers here..
43083          * @param {FCKeditor} this
43084          * @param {Object} the FCK object.
43085          */
43086         editorinit : true
43087     });
43088     
43089     
43090 };
43091 Roo.form.FCKeditor.editors = { };
43092 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
43093 {
43094     //defaultAutoCreate : {
43095     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
43096     //},
43097     // private
43098     /**
43099      * @cfg {Object} fck options - see fck manual for details.
43100      */
43101     fckconfig : false,
43102     
43103     /**
43104      * @cfg {Object} fck toolbar set (Basic or Default)
43105      */
43106     toolbarSet : 'Basic',
43107     /**
43108      * @cfg {Object} fck BasePath
43109      */ 
43110     basePath : '/fckeditor/',
43111     
43112     
43113     frame : false,
43114     
43115     value : '',
43116     
43117    
43118     onRender : function(ct, position)
43119     {
43120         if(!this.el){
43121             this.defaultAutoCreate = {
43122                 tag: "textarea",
43123                 style:"width:300px;height:60px;",
43124                 autocomplete: "off"
43125             };
43126         }
43127         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
43128         /*
43129         if(this.grow){
43130             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
43131             if(this.preventScrollbars){
43132                 this.el.setStyle("overflow", "hidden");
43133             }
43134             this.el.setHeight(this.growMin);
43135         }
43136         */
43137         //console.log('onrender' + this.getId() );
43138         Roo.form.FCKeditor.editors[this.getId()] = this;
43139          
43140
43141         this.replaceTextarea() ;
43142         
43143     },
43144     
43145     getEditor : function() {
43146         return this.fckEditor;
43147     },
43148     /**
43149      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43150      * @param {Mixed} value The value to set
43151      */
43152     
43153     
43154     setValue : function(value)
43155     {
43156         //console.log('setValue: ' + value);
43157         
43158         if(typeof(value) == 'undefined') { // not sure why this is happending...
43159             return;
43160         }
43161         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43162         
43163         //if(!this.el || !this.getEditor()) {
43164         //    this.value = value;
43165             //this.setValue.defer(100,this,[value]);    
43166         //    return;
43167         //} 
43168         
43169         if(!this.getEditor()) {
43170             return;
43171         }
43172         
43173         this.getEditor().SetData(value);
43174         
43175         //
43176
43177     },
43178
43179     /**
43180      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43181      * @return {Mixed} value The field value
43182      */
43183     getValue : function()
43184     {
43185         
43186         if (this.frame && this.frame.dom.style.display == 'none') {
43187             return Roo.form.FCKeditor.superclass.getValue.call(this);
43188         }
43189         
43190         if(!this.el || !this.getEditor()) {
43191            
43192            // this.getValue.defer(100,this); 
43193             return this.value;
43194         }
43195        
43196         
43197         var value=this.getEditor().GetData();
43198         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43199         return Roo.form.FCKeditor.superclass.getValue.call(this);
43200         
43201
43202     },
43203
43204     /**
43205      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43206      * @return {Mixed} value The field value
43207      */
43208     getRawValue : function()
43209     {
43210         if (this.frame && this.frame.dom.style.display == 'none') {
43211             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43212         }
43213         
43214         if(!this.el || !this.getEditor()) {
43215             //this.getRawValue.defer(100,this); 
43216             return this.value;
43217             return;
43218         }
43219         
43220         
43221         
43222         var value=this.getEditor().GetData();
43223         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43224         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43225          
43226     },
43227     
43228     setSize : function(w,h) {
43229         
43230         
43231         
43232         //if (this.frame && this.frame.dom.style.display == 'none') {
43233         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43234         //    return;
43235         //}
43236         //if(!this.el || !this.getEditor()) {
43237         //    this.setSize.defer(100,this, [w,h]); 
43238         //    return;
43239         //}
43240         
43241         
43242         
43243         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43244         
43245         this.frame.dom.setAttribute('width', w);
43246         this.frame.dom.setAttribute('height', h);
43247         this.frame.setSize(w,h);
43248         
43249     },
43250     
43251     toggleSourceEdit : function(value) {
43252         
43253       
43254          
43255         this.el.dom.style.display = value ? '' : 'none';
43256         this.frame.dom.style.display = value ?  'none' : '';
43257         
43258     },
43259     
43260     
43261     focus: function(tag)
43262     {
43263         if (this.frame.dom.style.display == 'none') {
43264             return Roo.form.FCKeditor.superclass.focus.call(this);
43265         }
43266         if(!this.el || !this.getEditor()) {
43267             this.focus.defer(100,this, [tag]); 
43268             return;
43269         }
43270         
43271         
43272         
43273         
43274         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43275         this.getEditor().Focus();
43276         if (tgs.length) {
43277             if (!this.getEditor().Selection.GetSelection()) {
43278                 this.focus.defer(100,this, [tag]); 
43279                 return;
43280             }
43281             
43282             
43283             var r = this.getEditor().EditorDocument.createRange();
43284             r.setStart(tgs[0],0);
43285             r.setEnd(tgs[0],0);
43286             this.getEditor().Selection.GetSelection().removeAllRanges();
43287             this.getEditor().Selection.GetSelection().addRange(r);
43288             this.getEditor().Focus();
43289         }
43290         
43291     },
43292     
43293     
43294     
43295     replaceTextarea : function()
43296     {
43297         if ( document.getElementById( this.getId() + '___Frame' ) )
43298             return ;
43299         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43300         //{
43301             // We must check the elements firstly using the Id and then the name.
43302         var oTextarea = document.getElementById( this.getId() );
43303         
43304         var colElementsByName = document.getElementsByName( this.getId() ) ;
43305          
43306         oTextarea.style.display = 'none' ;
43307
43308         if ( oTextarea.tabIndex ) {            
43309             this.TabIndex = oTextarea.tabIndex ;
43310         }
43311         
43312         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43313         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43314         this.frame = Roo.get(this.getId() + '___Frame')
43315     },
43316     
43317     _getConfigHtml : function()
43318     {
43319         var sConfig = '' ;
43320
43321         for ( var o in this.fckconfig ) {
43322             sConfig += sConfig.length > 0  ? '&amp;' : '';
43323             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43324         }
43325
43326         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43327     },
43328     
43329     
43330     _getIFrameHtml : function()
43331     {
43332         var sFile = 'fckeditor.html' ;
43333         /* no idea what this is about..
43334         try
43335         {
43336             if ( (/fcksource=true/i).test( window.top.location.search ) )
43337                 sFile = 'fckeditor.original.html' ;
43338         }
43339         catch (e) { 
43340         */
43341
43342         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43343         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43344         
43345         
43346         var html = '<iframe id="' + this.getId() +
43347             '___Frame" src="' + sLink +
43348             '" width="' + this.width +
43349             '" height="' + this.height + '"' +
43350             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43351             ' frameborder="0" scrolling="no"></iframe>' ;
43352
43353         return html ;
43354     },
43355     
43356     _insertHtmlBefore : function( html, element )
43357     {
43358         if ( element.insertAdjacentHTML )       {
43359             // IE
43360             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43361         } else { // Gecko
43362             var oRange = document.createRange() ;
43363             oRange.setStartBefore( element ) ;
43364             var oFragment = oRange.createContextualFragment( html );
43365             element.parentNode.insertBefore( oFragment, element ) ;
43366         }
43367     }
43368     
43369     
43370   
43371     
43372     
43373     
43374     
43375
43376 });
43377
43378 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43379
43380 function FCKeditor_OnComplete(editorInstance){
43381     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43382     f.fckEditor = editorInstance;
43383     //console.log("loaded");
43384     f.fireEvent('editorinit', f, editorInstance);
43385
43386   
43387
43388  
43389
43390
43391
43392
43393
43394
43395
43396
43397
43398
43399
43400
43401
43402
43403
43404 //<script type="text/javascript">
43405 /**
43406  * @class Roo.form.GridField
43407  * @extends Roo.form.Field
43408  * Embed a grid (or editable grid into a form)
43409  * STATUS ALPHA
43410  * 
43411  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43412  * it needs 
43413  * xgrid.store = Roo.data.Store
43414  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43415  * xgrid.store.reader = Roo.data.JsonReader 
43416  * 
43417  * 
43418  * @constructor
43419  * Creates a new GridField
43420  * @param {Object} config Configuration options
43421  */
43422 Roo.form.GridField = function(config){
43423     Roo.form.GridField.superclass.constructor.call(this, config);
43424      
43425 };
43426
43427 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43428     /**
43429      * @cfg {Number} width  - used to restrict width of grid..
43430      */
43431     width : 100,
43432     /**
43433      * @cfg {Number} height - used to restrict height of grid..
43434      */
43435     height : 50,
43436      /**
43437      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43438          * 
43439          *}
43440      */
43441     xgrid : false, 
43442     /**
43443      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43444      * {tag: "input", type: "checkbox", autocomplete: "off"})
43445      */
43446    // defaultAutoCreate : { tag: 'div' },
43447     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43448     /**
43449      * @cfg {String} addTitle Text to include for adding a title.
43450      */
43451     addTitle : false,
43452     //
43453     onResize : function(){
43454         Roo.form.Field.superclass.onResize.apply(this, arguments);
43455     },
43456
43457     initEvents : function(){
43458         // Roo.form.Checkbox.superclass.initEvents.call(this);
43459         // has no events...
43460        
43461     },
43462
43463
43464     getResizeEl : function(){
43465         return this.wrap;
43466     },
43467
43468     getPositionEl : function(){
43469         return this.wrap;
43470     },
43471
43472     // private
43473     onRender : function(ct, position){
43474         
43475         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43476         var style = this.style;
43477         delete this.style;
43478         
43479         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43480         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43481         this.viewEl = this.wrap.createChild({ tag: 'div' });
43482         if (style) {
43483             this.viewEl.applyStyles(style);
43484         }
43485         if (this.width) {
43486             this.viewEl.setWidth(this.width);
43487         }
43488         if (this.height) {
43489             this.viewEl.setHeight(this.height);
43490         }
43491         //if(this.inputValue !== undefined){
43492         //this.setValue(this.value);
43493         
43494         
43495         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43496         
43497         
43498         this.grid.render();
43499         this.grid.getDataSource().on('remove', this.refreshValue, this);
43500         this.grid.getDataSource().on('update', this.refreshValue, this);
43501         this.grid.on('afteredit', this.refreshValue, this);
43502  
43503     },
43504      
43505     
43506     /**
43507      * Sets the value of the item. 
43508      * @param {String} either an object  or a string..
43509      */
43510     setValue : function(v){
43511         //this.value = v;
43512         v = v || []; // empty set..
43513         // this does not seem smart - it really only affects memoryproxy grids..
43514         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43515             var ds = this.grid.getDataSource();
43516             // assumes a json reader..
43517             var data = {}
43518             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43519             ds.loadData( data);
43520         }
43521         // clear selection so it does not get stale.
43522         if (this.grid.sm) { 
43523             this.grid.sm.clearSelections();
43524         }
43525         
43526         Roo.form.GridField.superclass.setValue.call(this, v);
43527         this.refreshValue();
43528         // should load data in the grid really....
43529     },
43530     
43531     // private
43532     refreshValue: function() {
43533          var val = [];
43534         this.grid.getDataSource().each(function(r) {
43535             val.push(r.data);
43536         });
43537         this.el.dom.value = Roo.encode(val);
43538     }
43539     
43540      
43541     
43542     
43543 });/*
43544  * Based on:
43545  * Ext JS Library 1.1.1
43546  * Copyright(c) 2006-2007, Ext JS, LLC.
43547  *
43548  * Originally Released Under LGPL - original licence link has changed is not relivant.
43549  *
43550  * Fork - LGPL
43551  * <script type="text/javascript">
43552  */
43553 /**
43554  * @class Roo.form.DisplayField
43555  * @extends Roo.form.Field
43556  * A generic Field to display non-editable data.
43557  * @constructor
43558  * Creates a new Display Field item.
43559  * @param {Object} config Configuration options
43560  */
43561 Roo.form.DisplayField = function(config){
43562     Roo.form.DisplayField.superclass.constructor.call(this, config);
43563     
43564 };
43565
43566 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43567     inputType:      'hidden',
43568     allowBlank:     true,
43569     readOnly:         true,
43570     
43571  
43572     /**
43573      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43574      */
43575     focusClass : undefined,
43576     /**
43577      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43578      */
43579     fieldClass: 'x-form-field',
43580     
43581      /**
43582      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43583      */
43584     valueRenderer: undefined,
43585     
43586     width: 100,
43587     /**
43588      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43589      * {tag: "input", type: "checkbox", autocomplete: "off"})
43590      */
43591      
43592  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43593
43594     onResize : function(){
43595         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43596         
43597     },
43598
43599     initEvents : function(){
43600         // Roo.form.Checkbox.superclass.initEvents.call(this);
43601         // has no events...
43602        
43603     },
43604
43605
43606     getResizeEl : function(){
43607         return this.wrap;
43608     },
43609
43610     getPositionEl : function(){
43611         return this.wrap;
43612     },
43613
43614     // private
43615     onRender : function(ct, position){
43616         
43617         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43618         //if(this.inputValue !== undefined){
43619         this.wrap = this.el.wrap();
43620         
43621         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43622         
43623         if (this.bodyStyle) {
43624             this.viewEl.applyStyles(this.bodyStyle);
43625         }
43626         //this.viewEl.setStyle('padding', '2px');
43627         
43628         this.setValue(this.value);
43629         
43630     },
43631 /*
43632     // private
43633     initValue : Roo.emptyFn,
43634
43635   */
43636
43637         // private
43638     onClick : function(){
43639         
43640     },
43641
43642     /**
43643      * Sets the checked state of the checkbox.
43644      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43645      */
43646     setValue : function(v){
43647         this.value = v;
43648         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43649         // this might be called before we have a dom element..
43650         if (!this.viewEl) {
43651             return;
43652         }
43653         this.viewEl.dom.innerHTML = html;
43654         Roo.form.DisplayField.superclass.setValue.call(this, v);
43655
43656     }
43657 });/*
43658  * 
43659  * Licence- LGPL
43660  * 
43661  */
43662
43663 /**
43664  * @class Roo.form.DayPicker
43665  * @extends Roo.form.Field
43666  * A Day picker show [M] [T] [W] ....
43667  * @constructor
43668  * Creates a new Day Picker
43669  * @param {Object} config Configuration options
43670  */
43671 Roo.form.DayPicker= function(config){
43672     Roo.form.DayPicker.superclass.constructor.call(this, config);
43673      
43674 };
43675
43676 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43677     /**
43678      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43679      */
43680     focusClass : undefined,
43681     /**
43682      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43683      */
43684     fieldClass: "x-form-field",
43685    
43686     /**
43687      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43688      * {tag: "input", type: "checkbox", autocomplete: "off"})
43689      */
43690     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43691     
43692    
43693     actionMode : 'viewEl', 
43694     //
43695     // private
43696  
43697     inputType : 'hidden',
43698     
43699      
43700     inputElement: false, // real input element?
43701     basedOn: false, // ????
43702     
43703     isFormField: true, // not sure where this is needed!!!!
43704
43705     onResize : function(){
43706         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43707         if(!this.boxLabel){
43708             this.el.alignTo(this.wrap, 'c-c');
43709         }
43710     },
43711
43712     initEvents : function(){
43713         Roo.form.Checkbox.superclass.initEvents.call(this);
43714         this.el.on("click", this.onClick,  this);
43715         this.el.on("change", this.onClick,  this);
43716     },
43717
43718
43719     getResizeEl : function(){
43720         return this.wrap;
43721     },
43722
43723     getPositionEl : function(){
43724         return this.wrap;
43725     },
43726
43727     
43728     // private
43729     onRender : function(ct, position){
43730         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43731        
43732         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43733         
43734         var r1 = '<table><tr>';
43735         var r2 = '<tr class="x-form-daypick-icons">';
43736         for (var i=0; i < 7; i++) {
43737             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43738             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43739         }
43740         
43741         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43742         viewEl.select('img').on('click', this.onClick, this);
43743         this.viewEl = viewEl;   
43744         
43745         
43746         // this will not work on Chrome!!!
43747         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43748         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43749         
43750         
43751           
43752
43753     },
43754
43755     // private
43756     initValue : Roo.emptyFn,
43757
43758     /**
43759      * Returns the checked state of the checkbox.
43760      * @return {Boolean} True if checked, else false
43761      */
43762     getValue : function(){
43763         return this.el.dom.value;
43764         
43765     },
43766
43767         // private
43768     onClick : function(e){ 
43769         //this.setChecked(!this.checked);
43770         Roo.get(e.target).toggleClass('x-menu-item-checked');
43771         this.refreshValue();
43772         //if(this.el.dom.checked != this.checked){
43773         //    this.setValue(this.el.dom.checked);
43774        // }
43775     },
43776     
43777     // private
43778     refreshValue : function()
43779     {
43780         var val = '';
43781         this.viewEl.select('img',true).each(function(e,i,n)  {
43782             val += e.is(".x-menu-item-checked") ? String(n) : '';
43783         });
43784         this.setValue(val, true);
43785     },
43786
43787     /**
43788      * Sets the checked state of the checkbox.
43789      * On is always based on a string comparison between inputValue and the param.
43790      * @param {Boolean/String} value - the value to set 
43791      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43792      */
43793     setValue : function(v,suppressEvent){
43794         if (!this.el.dom) {
43795             return;
43796         }
43797         var old = this.el.dom.value ;
43798         this.el.dom.value = v;
43799         if (suppressEvent) {
43800             return ;
43801         }
43802          
43803         // update display..
43804         this.viewEl.select('img',true).each(function(e,i,n)  {
43805             
43806             var on = e.is(".x-menu-item-checked");
43807             var newv = v.indexOf(String(n)) > -1;
43808             if (on != newv) {
43809                 e.toggleClass('x-menu-item-checked');
43810             }
43811             
43812         });
43813         
43814         
43815         this.fireEvent('change', this, v, old);
43816         
43817         
43818     },
43819    
43820     // handle setting of hidden value by some other method!!?!?
43821     setFromHidden: function()
43822     {
43823         if(!this.el){
43824             return;
43825         }
43826         //console.log("SET FROM HIDDEN");
43827         //alert('setFrom hidden');
43828         this.setValue(this.el.dom.value);
43829     },
43830     
43831     onDestroy : function()
43832     {
43833         if(this.viewEl){
43834             Roo.get(this.viewEl).remove();
43835         }
43836          
43837         Roo.form.DayPicker.superclass.onDestroy.call(this);
43838     }
43839
43840 });/*
43841  * RooJS Library 1.1.1
43842  * Copyright(c) 2008-2011  Alan Knowles
43843  *
43844  * License - LGPL
43845  */
43846  
43847
43848 /**
43849  * @class Roo.form.ComboCheck
43850  * @extends Roo.form.ComboBox
43851  * A combobox for multiple select items.
43852  *
43853  * FIXME - could do with a reset button..
43854  * 
43855  * @constructor
43856  * Create a new ComboCheck
43857  * @param {Object} config Configuration options
43858  */
43859 Roo.form.ComboCheck = function(config){
43860     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43861     // should verify some data...
43862     // like
43863     // hiddenName = required..
43864     // displayField = required
43865     // valudField == required
43866     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43867     var _t = this;
43868     Roo.each(req, function(e) {
43869         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43870             throw "Roo.form.ComboCheck : missing value for: " + e;
43871         }
43872     });
43873     
43874     
43875 };
43876
43877 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43878      
43879      
43880     editable : false,
43881      
43882     selectedClass: 'x-menu-item-checked', 
43883     
43884     // private
43885     onRender : function(ct, position){
43886         var _t = this;
43887         
43888         
43889         
43890         if(!this.tpl){
43891             var cls = 'x-combo-list';
43892
43893             
43894             this.tpl =  new Roo.Template({
43895                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43896                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43897                    '<span>{' + this.displayField + '}</span>' +
43898                     '</div>' 
43899                 
43900             });
43901         }
43902  
43903         
43904         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43905         this.view.singleSelect = false;
43906         this.view.multiSelect = true;
43907         this.view.toggleSelect = true;
43908         this.pageTb.add(new Roo.Toolbar.Fill(), {
43909             
43910             text: 'Done',
43911             handler: function()
43912             {
43913                 _t.collapse();
43914             }
43915         });
43916     },
43917     
43918     onViewOver : function(e, t){
43919         // do nothing...
43920         return;
43921         
43922     },
43923     
43924     onViewClick : function(doFocus,index){
43925         return;
43926         
43927     },
43928     select: function () {
43929         //Roo.log("SELECT CALLED");
43930     },
43931      
43932     selectByValue : function(xv, scrollIntoView){
43933         var ar = this.getValueArray();
43934         var sels = [];
43935         
43936         Roo.each(ar, function(v) {
43937             if(v === undefined || v === null){
43938                 return;
43939             }
43940             var r = this.findRecord(this.valueField, v);
43941             if(r){
43942                 sels.push(this.store.indexOf(r))
43943                 
43944             }
43945         },this);
43946         this.view.select(sels);
43947         return false;
43948     },
43949     
43950     
43951     
43952     onSelect : function(record, index){
43953        // Roo.log("onselect Called");
43954        // this is only called by the clear button now..
43955         this.view.clearSelections();
43956         this.setValue('[]');
43957         if (this.value != this.valueBefore) {
43958             this.fireEvent('change', this, this.value, this.valueBefore);
43959         }
43960     },
43961     getValueArray : function()
43962     {
43963         var ar = [] ;
43964         
43965         try {
43966             //Roo.log(this.value);
43967             if (typeof(this.value) == 'undefined') {
43968                 return [];
43969             }
43970             var ar = Roo.decode(this.value);
43971             return  ar instanceof Array ? ar : []; //?? valid?
43972             
43973         } catch(e) {
43974             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43975             return [];
43976         }
43977          
43978     },
43979     expand : function ()
43980     {
43981         Roo.form.ComboCheck.superclass.expand.call(this);
43982         this.valueBefore = this.value;
43983         
43984
43985     },
43986     
43987     collapse : function(){
43988         Roo.form.ComboCheck.superclass.collapse.call(this);
43989         var sl = this.view.getSelectedIndexes();
43990         var st = this.store;
43991         var nv = [];
43992         var tv = [];
43993         var r;
43994         Roo.each(sl, function(i) {
43995             r = st.getAt(i);
43996             nv.push(r.get(this.valueField));
43997         },this);
43998         this.setValue(Roo.encode(nv));
43999         if (this.value != this.valueBefore) {
44000
44001             this.fireEvent('change', this, this.value, this.valueBefore);
44002         }
44003         
44004     },
44005     
44006     setValue : function(v){
44007         // Roo.log(v);
44008         this.value = v;
44009         
44010         var vals = this.getValueArray();
44011         var tv = [];
44012         Roo.each(vals, function(k) {
44013             var r = this.findRecord(this.valueField, k);
44014             if(r){
44015                 tv.push(r.data[this.displayField]);
44016             }else if(this.valueNotFoundText !== undefined){
44017                 tv.push( this.valueNotFoundText );
44018             }
44019         },this);
44020        // Roo.log(tv);
44021         
44022         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
44023         this.hiddenField.value = v;
44024         this.value = v;
44025     }
44026     
44027 });//<script type="text/javasscript">
44028  
44029
44030 /**
44031  * @class Roo.DDView
44032  * A DnD enabled version of Roo.View.
44033  * @param {Element/String} container The Element in which to create the View.
44034  * @param {String} tpl The template string used to create the markup for each element of the View
44035  * @param {Object} config The configuration properties. These include all the config options of
44036  * {@link Roo.View} plus some specific to this class.<br>
44037  * <p>
44038  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
44039  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
44040  * <p>
44041  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
44042 .x-view-drag-insert-above {
44043         border-top:1px dotted #3366cc;
44044 }
44045 .x-view-drag-insert-below {
44046         border-bottom:1px dotted #3366cc;
44047 }
44048 </code></pre>
44049  * 
44050  */
44051  
44052 Roo.DDView = function(container, tpl, config) {
44053     Roo.DDView.superclass.constructor.apply(this, arguments);
44054     this.getEl().setStyle("outline", "0px none");
44055     this.getEl().unselectable();
44056     if (this.dragGroup) {
44057                 this.setDraggable(this.dragGroup.split(","));
44058     }
44059     if (this.dropGroup) {
44060                 this.setDroppable(this.dropGroup.split(","));
44061     }
44062     if (this.deletable) {
44063         this.setDeletable();
44064     }
44065     this.isDirtyFlag = false;
44066         this.addEvents({
44067                 "drop" : true
44068         });
44069 };
44070
44071 Roo.extend(Roo.DDView, Roo.View, {
44072 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
44073 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
44074 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
44075 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
44076
44077         isFormField: true,
44078
44079         reset: Roo.emptyFn,
44080         
44081         clearInvalid: Roo.form.Field.prototype.clearInvalid,
44082
44083         validate: function() {
44084                 return true;
44085         },
44086         
44087         destroy: function() {
44088                 this.purgeListeners();
44089                 this.getEl.removeAllListeners();
44090                 this.getEl().remove();
44091                 if (this.dragZone) {
44092                         if (this.dragZone.destroy) {
44093                                 this.dragZone.destroy();
44094                         }
44095                 }
44096                 if (this.dropZone) {
44097                         if (this.dropZone.destroy) {
44098                                 this.dropZone.destroy();
44099                         }
44100                 }
44101         },
44102
44103 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
44104         getName: function() {
44105                 return this.name;
44106         },
44107
44108 /**     Loads the View from a JSON string representing the Records to put into the Store. */
44109         setValue: function(v) {
44110                 if (!this.store) {
44111                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
44112                 }
44113                 var data = {};
44114                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
44115                 this.store.proxy = new Roo.data.MemoryProxy(data);
44116                 this.store.load();
44117         },
44118
44119 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
44120         getValue: function() {
44121                 var result = '(';
44122                 this.store.each(function(rec) {
44123                         result += rec.id + ',';
44124                 });
44125                 return result.substr(0, result.length - 1) + ')';
44126         },
44127         
44128         getIds: function() {
44129                 var i = 0, result = new Array(this.store.getCount());
44130                 this.store.each(function(rec) {
44131                         result[i++] = rec.id;
44132                 });
44133                 return result;
44134         },
44135         
44136         isDirty: function() {
44137                 return this.isDirtyFlag;
44138         },
44139
44140 /**
44141  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
44142  *      whole Element becomes the target, and this causes the drop gesture to append.
44143  */
44144     getTargetFromEvent : function(e) {
44145                 var target = e.getTarget();
44146                 while ((target !== null) && (target.parentNode != this.el.dom)) {
44147                 target = target.parentNode;
44148                 }
44149                 if (!target) {
44150                         target = this.el.dom.lastChild || this.el.dom;
44151                 }
44152                 return target;
44153     },
44154
44155 /**
44156  *      Create the drag data which consists of an object which has the property "ddel" as
44157  *      the drag proxy element. 
44158  */
44159     getDragData : function(e) {
44160         var target = this.findItemFromChild(e.getTarget());
44161                 if(target) {
44162                         this.handleSelection(e);
44163                         var selNodes = this.getSelectedNodes();
44164             var dragData = {
44165                 source: this,
44166                 copy: this.copy || (this.allowCopy && e.ctrlKey),
44167                 nodes: selNodes,
44168                 records: []
44169                         };
44170                         var selectedIndices = this.getSelectedIndexes();
44171                         for (var i = 0; i < selectedIndices.length; i++) {
44172                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
44173                         }
44174                         if (selNodes.length == 1) {
44175                                 dragData.ddel = target.cloneNode(true); // the div element
44176                         } else {
44177                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44178                                 div.className = 'multi-proxy';
44179                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44180                                         div.appendChild(selNodes[i].cloneNode(true));
44181                                 }
44182                                 dragData.ddel = div;
44183                         }
44184             //console.log(dragData)
44185             //console.log(dragData.ddel.innerHTML)
44186                         return dragData;
44187                 }
44188         //console.log('nodragData')
44189                 return false;
44190     },
44191     
44192 /**     Specify to which ddGroup items in this DDView may be dragged. */
44193     setDraggable: function(ddGroup) {
44194         if (ddGroup instanceof Array) {
44195                 Roo.each(ddGroup, this.setDraggable, this);
44196                 return;
44197         }
44198         if (this.dragZone) {
44199                 this.dragZone.addToGroup(ddGroup);
44200         } else {
44201                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44202                                 containerScroll: true,
44203                                 ddGroup: ddGroup 
44204
44205                         });
44206 //                      Draggability implies selection. DragZone's mousedown selects the element.
44207                         if (!this.multiSelect) { this.singleSelect = true; }
44208
44209 //                      Wire the DragZone's handlers up to methods in *this*
44210                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44211                 }
44212     },
44213
44214 /**     Specify from which ddGroup this DDView accepts drops. */
44215     setDroppable: function(ddGroup) {
44216         if (ddGroup instanceof Array) {
44217                 Roo.each(ddGroup, this.setDroppable, this);
44218                 return;
44219         }
44220         if (this.dropZone) {
44221                 this.dropZone.addToGroup(ddGroup);
44222         } else {
44223                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44224                                 containerScroll: true,
44225                                 ddGroup: ddGroup
44226                         });
44227
44228 //                      Wire the DropZone's handlers up to methods in *this*
44229                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44230                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44231                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44232                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44233                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44234                 }
44235     },
44236
44237 /**     Decide whether to drop above or below a View node. */
44238     getDropPoint : function(e, n, dd){
44239         if (n == this.el.dom) { return "above"; }
44240                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44241                 var c = t + (b - t) / 2;
44242                 var y = Roo.lib.Event.getPageY(e);
44243                 if(y <= c) {
44244                         return "above";
44245                 }else{
44246                         return "below";
44247                 }
44248     },
44249
44250     onNodeEnter : function(n, dd, e, data){
44251                 return false;
44252     },
44253     
44254     onNodeOver : function(n, dd, e, data){
44255                 var pt = this.getDropPoint(e, n, dd);
44256                 // set the insert point style on the target node
44257                 var dragElClass = this.dropNotAllowed;
44258                 if (pt) {
44259                         var targetElClass;
44260                         if (pt == "above"){
44261                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44262                                 targetElClass = "x-view-drag-insert-above";
44263                         } else {
44264                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44265                                 targetElClass = "x-view-drag-insert-below";
44266                         }
44267                         if (this.lastInsertClass != targetElClass){
44268                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44269                                 this.lastInsertClass = targetElClass;
44270                         }
44271                 }
44272                 return dragElClass;
44273         },
44274
44275     onNodeOut : function(n, dd, e, data){
44276                 this.removeDropIndicators(n);
44277     },
44278
44279     onNodeDrop : function(n, dd, e, data){
44280         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44281                 return false;
44282         }
44283         var pt = this.getDropPoint(e, n, dd);
44284                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44285                 if (pt == "below") { insertAt++; }
44286                 for (var i = 0; i < data.records.length; i++) {
44287                         var r = data.records[i];
44288                         var dup = this.store.getById(r.id);
44289                         if (dup && (dd != this.dragZone)) {
44290                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44291                         } else {
44292                                 if (data.copy) {
44293                                         this.store.insert(insertAt++, r.copy());
44294                                 } else {
44295                                         data.source.isDirtyFlag = true;
44296                                         r.store.remove(r);
44297                                         this.store.insert(insertAt++, r);
44298                                 }
44299                                 this.isDirtyFlag = true;
44300                         }
44301                 }
44302                 this.dragZone.cachedTarget = null;
44303                 return true;
44304     },
44305
44306     removeDropIndicators : function(n){
44307                 if(n){
44308                         Roo.fly(n).removeClass([
44309                                 "x-view-drag-insert-above",
44310                                 "x-view-drag-insert-below"]);
44311                         this.lastInsertClass = "_noclass";
44312                 }
44313     },
44314
44315 /**
44316  *      Utility method. Add a delete option to the DDView's context menu.
44317  *      @param {String} imageUrl The URL of the "delete" icon image.
44318  */
44319         setDeletable: function(imageUrl) {
44320                 if (!this.singleSelect && !this.multiSelect) {
44321                         this.singleSelect = true;
44322                 }
44323                 var c = this.getContextMenu();
44324                 this.contextMenu.on("itemclick", function(item) {
44325                         switch (item.id) {
44326                                 case "delete":
44327                                         this.remove(this.getSelectedIndexes());
44328                                         break;
44329                         }
44330                 }, this);
44331                 this.contextMenu.add({
44332                         icon: imageUrl,
44333                         id: "delete",
44334                         text: 'Delete'
44335                 });
44336         },
44337         
44338 /**     Return the context menu for this DDView. */
44339         getContextMenu: function() {
44340                 if (!this.contextMenu) {
44341 //                      Create the View's context menu
44342                         this.contextMenu = new Roo.menu.Menu({
44343                                 id: this.id + "-contextmenu"
44344                         });
44345                         this.el.on("contextmenu", this.showContextMenu, this);
44346                 }
44347                 return this.contextMenu;
44348         },
44349         
44350         disableContextMenu: function() {
44351                 if (this.contextMenu) {
44352                         this.el.un("contextmenu", this.showContextMenu, this);
44353                 }
44354         },
44355
44356         showContextMenu: function(e, item) {
44357         item = this.findItemFromChild(e.getTarget());
44358                 if (item) {
44359                         e.stopEvent();
44360                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44361                         this.contextMenu.showAt(e.getXY());
44362             }
44363     },
44364
44365 /**
44366  *      Remove {@link Roo.data.Record}s at the specified indices.
44367  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44368  */
44369     remove: function(selectedIndices) {
44370                 selectedIndices = [].concat(selectedIndices);
44371                 for (var i = 0; i < selectedIndices.length; i++) {
44372                         var rec = this.store.getAt(selectedIndices[i]);
44373                         this.store.remove(rec);
44374                 }
44375     },
44376
44377 /**
44378  *      Double click fires the event, but also, if this is draggable, and there is only one other
44379  *      related DropZone, it transfers the selected node.
44380  */
44381     onDblClick : function(e){
44382         var item = this.findItemFromChild(e.getTarget());
44383         if(item){
44384             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44385                 return false;
44386             }
44387             if (this.dragGroup) {
44388                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44389                     while (targets.indexOf(this.dropZone) > -1) {
44390                             targets.remove(this.dropZone);
44391                                 }
44392                     if (targets.length == 1) {
44393                                         this.dragZone.cachedTarget = null;
44394                         var el = Roo.get(targets[0].getEl());
44395                         var box = el.getBox(true);
44396                         targets[0].onNodeDrop(el.dom, {
44397                                 target: el.dom,
44398                                 xy: [box.x, box.y + box.height - 1]
44399                         }, null, this.getDragData(e));
44400                     }
44401                 }
44402         }
44403     },
44404     
44405     handleSelection: function(e) {
44406                 this.dragZone.cachedTarget = null;
44407         var item = this.findItemFromChild(e.getTarget());
44408         if (!item) {
44409                 this.clearSelections(true);
44410                 return;
44411         }
44412                 if (item && (this.multiSelect || this.singleSelect)){
44413                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44414                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44415                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44416                                 this.unselect(item);
44417                         } else {
44418                                 this.select(item, this.multiSelect && e.ctrlKey);
44419                                 this.lastSelection = item;
44420                         }
44421                 }
44422     },
44423
44424     onItemClick : function(item, index, e){
44425                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44426                         return false;
44427                 }
44428                 return true;
44429     },
44430
44431     unselect : function(nodeInfo, suppressEvent){
44432                 var node = this.getNode(nodeInfo);
44433                 if(node && this.isSelected(node)){
44434                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44435                                 Roo.fly(node).removeClass(this.selectedClass);
44436                                 this.selections.remove(node);
44437                                 if(!suppressEvent){
44438                                         this.fireEvent("selectionchange", this, this.selections);
44439                                 }
44440                         }
44441                 }
44442     }
44443 });
44444 /*
44445  * Based on:
44446  * Ext JS Library 1.1.1
44447  * Copyright(c) 2006-2007, Ext JS, LLC.
44448  *
44449  * Originally Released Under LGPL - original licence link has changed is not relivant.
44450  *
44451  * Fork - LGPL
44452  * <script type="text/javascript">
44453  */
44454  
44455 /**
44456  * @class Roo.LayoutManager
44457  * @extends Roo.util.Observable
44458  * Base class for layout managers.
44459  */
44460 Roo.LayoutManager = function(container, config){
44461     Roo.LayoutManager.superclass.constructor.call(this);
44462     this.el = Roo.get(container);
44463     // ie scrollbar fix
44464     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44465         document.body.scroll = "no";
44466     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44467         this.el.position('relative');
44468     }
44469     this.id = this.el.id;
44470     this.el.addClass("x-layout-container");
44471     /** false to disable window resize monitoring @type Boolean */
44472     this.monitorWindowResize = true;
44473     this.regions = {};
44474     this.addEvents({
44475         /**
44476          * @event layout
44477          * Fires when a layout is performed. 
44478          * @param {Roo.LayoutManager} this
44479          */
44480         "layout" : true,
44481         /**
44482          * @event regionresized
44483          * Fires when the user resizes a region. 
44484          * @param {Roo.LayoutRegion} region The resized region
44485          * @param {Number} newSize The new size (width for east/west, height for north/south)
44486          */
44487         "regionresized" : true,
44488         /**
44489          * @event regioncollapsed
44490          * Fires when a region is collapsed. 
44491          * @param {Roo.LayoutRegion} region The collapsed region
44492          */
44493         "regioncollapsed" : true,
44494         /**
44495          * @event regionexpanded
44496          * Fires when a region is expanded.  
44497          * @param {Roo.LayoutRegion} region The expanded region
44498          */
44499         "regionexpanded" : true
44500     });
44501     this.updating = false;
44502     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44503 };
44504
44505 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44506     /**
44507      * Returns true if this layout is currently being updated
44508      * @return {Boolean}
44509      */
44510     isUpdating : function(){
44511         return this.updating; 
44512     },
44513     
44514     /**
44515      * Suspend the LayoutManager from doing auto-layouts while
44516      * making multiple add or remove calls
44517      */
44518     beginUpdate : function(){
44519         this.updating = true;    
44520     },
44521     
44522     /**
44523      * Restore auto-layouts and optionally disable the manager from performing a layout
44524      * @param {Boolean} noLayout true to disable a layout update 
44525      */
44526     endUpdate : function(noLayout){
44527         this.updating = false;
44528         if(!noLayout){
44529             this.layout();
44530         }    
44531     },
44532     
44533     layout: function(){
44534         
44535     },
44536     
44537     onRegionResized : function(region, newSize){
44538         this.fireEvent("regionresized", region, newSize);
44539         this.layout();
44540     },
44541     
44542     onRegionCollapsed : function(region){
44543         this.fireEvent("regioncollapsed", region);
44544     },
44545     
44546     onRegionExpanded : function(region){
44547         this.fireEvent("regionexpanded", region);
44548     },
44549         
44550     /**
44551      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44552      * performs box-model adjustments.
44553      * @return {Object} The size as an object {width: (the width), height: (the height)}
44554      */
44555     getViewSize : function(){
44556         var size;
44557         if(this.el.dom != document.body){
44558             size = this.el.getSize();
44559         }else{
44560             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44561         }
44562         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44563         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44564         return size;
44565     },
44566     
44567     /**
44568      * Returns the Element this layout is bound to.
44569      * @return {Roo.Element}
44570      */
44571     getEl : function(){
44572         return this.el;
44573     },
44574     
44575     /**
44576      * Returns the specified region.
44577      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44578      * @return {Roo.LayoutRegion}
44579      */
44580     getRegion : function(target){
44581         return this.regions[target.toLowerCase()];
44582     },
44583     
44584     onWindowResize : function(){
44585         if(this.monitorWindowResize){
44586             this.layout();
44587         }
44588     }
44589 });/*
44590  * Based on:
44591  * Ext JS Library 1.1.1
44592  * Copyright(c) 2006-2007, Ext JS, LLC.
44593  *
44594  * Originally Released Under LGPL - original licence link has changed is not relivant.
44595  *
44596  * Fork - LGPL
44597  * <script type="text/javascript">
44598  */
44599 /**
44600  * @class Roo.BorderLayout
44601  * @extends Roo.LayoutManager
44602  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44603  * please see: <br><br>
44604  * <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>
44605  * <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>
44606  * Example:
44607  <pre><code>
44608  var layout = new Roo.BorderLayout(document.body, {
44609     north: {
44610         initialSize: 25,
44611         titlebar: false
44612     },
44613     west: {
44614         split:true,
44615         initialSize: 200,
44616         minSize: 175,
44617         maxSize: 400,
44618         titlebar: true,
44619         collapsible: true
44620     },
44621     east: {
44622         split:true,
44623         initialSize: 202,
44624         minSize: 175,
44625         maxSize: 400,
44626         titlebar: true,
44627         collapsible: true
44628     },
44629     south: {
44630         split:true,
44631         initialSize: 100,
44632         minSize: 100,
44633         maxSize: 200,
44634         titlebar: true,
44635         collapsible: true
44636     },
44637     center: {
44638         titlebar: true,
44639         autoScroll:true,
44640         resizeTabs: true,
44641         minTabWidth: 50,
44642         preferredTabWidth: 150
44643     }
44644 });
44645
44646 // shorthand
44647 var CP = Roo.ContentPanel;
44648
44649 layout.beginUpdate();
44650 layout.add("north", new CP("north", "North"));
44651 layout.add("south", new CP("south", {title: "South", closable: true}));
44652 layout.add("west", new CP("west", {title: "West"}));
44653 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44654 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44655 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44656 layout.getRegion("center").showPanel("center1");
44657 layout.endUpdate();
44658 </code></pre>
44659
44660 <b>The container the layout is rendered into can be either the body element or any other element.
44661 If it is not the body element, the container needs to either be an absolute positioned element,
44662 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44663 the container size if it is not the body element.</b>
44664
44665 * @constructor
44666 * Create a new BorderLayout
44667 * @param {String/HTMLElement/Element} container The container this layout is bound to
44668 * @param {Object} config Configuration options
44669  */
44670 Roo.BorderLayout = function(container, config){
44671     config = config || {};
44672     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44673     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44674     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44675         var target = this.factory.validRegions[i];
44676         if(config[target]){
44677             this.addRegion(target, config[target]);
44678         }
44679     }
44680 };
44681
44682 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44683     /**
44684      * Creates and adds a new region if it doesn't already exist.
44685      * @param {String} target The target region key (north, south, east, west or center).
44686      * @param {Object} config The regions config object
44687      * @return {BorderLayoutRegion} The new region
44688      */
44689     addRegion : function(target, config){
44690         if(!this.regions[target]){
44691             var r = this.factory.create(target, this, config);
44692             this.bindRegion(target, r);
44693         }
44694         return this.regions[target];
44695     },
44696
44697     // private (kinda)
44698     bindRegion : function(name, r){
44699         this.regions[name] = r;
44700         r.on("visibilitychange", this.layout, this);
44701         r.on("paneladded", this.layout, this);
44702         r.on("panelremoved", this.layout, this);
44703         r.on("invalidated", this.layout, this);
44704         r.on("resized", this.onRegionResized, this);
44705         r.on("collapsed", this.onRegionCollapsed, this);
44706         r.on("expanded", this.onRegionExpanded, this);
44707     },
44708
44709     /**
44710      * Performs a layout update.
44711      */
44712     layout : function(){
44713         if(this.updating) return;
44714         var size = this.getViewSize();
44715         var w = size.width;
44716         var h = size.height;
44717         var centerW = w;
44718         var centerH = h;
44719         var centerY = 0;
44720         var centerX = 0;
44721         //var x = 0, y = 0;
44722
44723         var rs = this.regions;
44724         var north = rs["north"];
44725         var south = rs["south"]; 
44726         var west = rs["west"];
44727         var east = rs["east"];
44728         var center = rs["center"];
44729         //if(this.hideOnLayout){ // not supported anymore
44730             //c.el.setStyle("display", "none");
44731         //}
44732         if(north && north.isVisible()){
44733             var b = north.getBox();
44734             var m = north.getMargins();
44735             b.width = w - (m.left+m.right);
44736             b.x = m.left;
44737             b.y = m.top;
44738             centerY = b.height + b.y + m.bottom;
44739             centerH -= centerY;
44740             north.updateBox(this.safeBox(b));
44741         }
44742         if(south && south.isVisible()){
44743             var b = south.getBox();
44744             var m = south.getMargins();
44745             b.width = w - (m.left+m.right);
44746             b.x = m.left;
44747             var totalHeight = (b.height + m.top + m.bottom);
44748             b.y = h - totalHeight + m.top;
44749             centerH -= totalHeight;
44750             south.updateBox(this.safeBox(b));
44751         }
44752         if(west && west.isVisible()){
44753             var b = west.getBox();
44754             var m = west.getMargins();
44755             b.height = centerH - (m.top+m.bottom);
44756             b.x = m.left;
44757             b.y = centerY + m.top;
44758             var totalWidth = (b.width + m.left + m.right);
44759             centerX += totalWidth;
44760             centerW -= totalWidth;
44761             west.updateBox(this.safeBox(b));
44762         }
44763         if(east && east.isVisible()){
44764             var b = east.getBox();
44765             var m = east.getMargins();
44766             b.height = centerH - (m.top+m.bottom);
44767             var totalWidth = (b.width + m.left + m.right);
44768             b.x = w - totalWidth + m.left;
44769             b.y = centerY + m.top;
44770             centerW -= totalWidth;
44771             east.updateBox(this.safeBox(b));
44772         }
44773         if(center){
44774             var m = center.getMargins();
44775             var centerBox = {
44776                 x: centerX + m.left,
44777                 y: centerY + m.top,
44778                 width: centerW - (m.left+m.right),
44779                 height: centerH - (m.top+m.bottom)
44780             };
44781             //if(this.hideOnLayout){
44782                 //center.el.setStyle("display", "block");
44783             //}
44784             center.updateBox(this.safeBox(centerBox));
44785         }
44786         this.el.repaint();
44787         this.fireEvent("layout", this);
44788     },
44789
44790     // private
44791     safeBox : function(box){
44792         box.width = Math.max(0, box.width);
44793         box.height = Math.max(0, box.height);
44794         return box;
44795     },
44796
44797     /**
44798      * Adds a ContentPanel (or subclass) to this layout.
44799      * @param {String} target The target region key (north, south, east, west or center).
44800      * @param {Roo.ContentPanel} panel The panel to add
44801      * @return {Roo.ContentPanel} The added panel
44802      */
44803     add : function(target, panel){
44804          
44805         target = target.toLowerCase();
44806         return this.regions[target].add(panel);
44807     },
44808
44809     /**
44810      * Remove a ContentPanel (or subclass) to this layout.
44811      * @param {String} target The target region key (north, south, east, west or center).
44812      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44813      * @return {Roo.ContentPanel} The removed panel
44814      */
44815     remove : function(target, panel){
44816         target = target.toLowerCase();
44817         return this.regions[target].remove(panel);
44818     },
44819
44820     /**
44821      * Searches all regions for a panel with the specified id
44822      * @param {String} panelId
44823      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44824      */
44825     findPanel : function(panelId){
44826         var rs = this.regions;
44827         for(var target in rs){
44828             if(typeof rs[target] != "function"){
44829                 var p = rs[target].getPanel(panelId);
44830                 if(p){
44831                     return p;
44832                 }
44833             }
44834         }
44835         return null;
44836     },
44837
44838     /**
44839      * Searches all regions for a panel with the specified id and activates (shows) it.
44840      * @param {String/ContentPanel} panelId The panels id or the panel itself
44841      * @return {Roo.ContentPanel} The shown panel or null
44842      */
44843     showPanel : function(panelId) {
44844       var rs = this.regions;
44845       for(var target in rs){
44846          var r = rs[target];
44847          if(typeof r != "function"){
44848             if(r.hasPanel(panelId)){
44849                return r.showPanel(panelId);
44850             }
44851          }
44852       }
44853       return null;
44854    },
44855
44856    /**
44857      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44858      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44859      */
44860     restoreState : function(provider){
44861         if(!provider){
44862             provider = Roo.state.Manager;
44863         }
44864         var sm = new Roo.LayoutStateManager();
44865         sm.init(this, provider);
44866     },
44867
44868     /**
44869      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44870      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44871      * a valid ContentPanel config object.  Example:
44872      * <pre><code>
44873 // Create the main layout
44874 var layout = new Roo.BorderLayout('main-ct', {
44875     west: {
44876         split:true,
44877         minSize: 175,
44878         titlebar: true
44879     },
44880     center: {
44881         title:'Components'
44882     }
44883 }, 'main-ct');
44884
44885 // Create and add multiple ContentPanels at once via configs
44886 layout.batchAdd({
44887    west: {
44888        id: 'source-files',
44889        autoCreate:true,
44890        title:'Ext Source Files',
44891        autoScroll:true,
44892        fitToFrame:true
44893    },
44894    center : {
44895        el: cview,
44896        autoScroll:true,
44897        fitToFrame:true,
44898        toolbar: tb,
44899        resizeEl:'cbody'
44900    }
44901 });
44902 </code></pre>
44903      * @param {Object} regions An object containing ContentPanel configs by region name
44904      */
44905     batchAdd : function(regions){
44906         this.beginUpdate();
44907         for(var rname in regions){
44908             var lr = this.regions[rname];
44909             if(lr){
44910                 this.addTypedPanels(lr, regions[rname]);
44911             }
44912         }
44913         this.endUpdate();
44914     },
44915
44916     // private
44917     addTypedPanels : function(lr, ps){
44918         if(typeof ps == 'string'){
44919             lr.add(new Roo.ContentPanel(ps));
44920         }
44921         else if(ps instanceof Array){
44922             for(var i =0, len = ps.length; i < len; i++){
44923                 this.addTypedPanels(lr, ps[i]);
44924             }
44925         }
44926         else if(!ps.events){ // raw config?
44927             var el = ps.el;
44928             delete ps.el; // prevent conflict
44929             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44930         }
44931         else {  // panel object assumed!
44932             lr.add(ps);
44933         }
44934     },
44935     /**
44936      * Adds a xtype elements to the layout.
44937      * <pre><code>
44938
44939 layout.addxtype({
44940        xtype : 'ContentPanel',
44941        region: 'west',
44942        items: [ .... ]
44943    }
44944 );
44945
44946 layout.addxtype({
44947         xtype : 'NestedLayoutPanel',
44948         region: 'west',
44949         layout: {
44950            center: { },
44951            west: { }   
44952         },
44953         items : [ ... list of content panels or nested layout panels.. ]
44954    }
44955 );
44956 </code></pre>
44957      * @param {Object} cfg Xtype definition of item to add.
44958      */
44959     addxtype : function(cfg)
44960     {
44961         // basically accepts a pannel...
44962         // can accept a layout region..!?!?
44963         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44964         
44965         if (!cfg.xtype.match(/Panel$/)) {
44966             return false;
44967         }
44968         var ret = false;
44969         
44970         if (typeof(cfg.region) == 'undefined') {
44971             Roo.log("Failed to add Panel, region was not set");
44972             Roo.log(cfg);
44973             return false;
44974         }
44975         var region = cfg.region;
44976         delete cfg.region;
44977         
44978           
44979         var xitems = [];
44980         if (cfg.items) {
44981             xitems = cfg.items;
44982             delete cfg.items;
44983         }
44984         var nb = false;
44985         
44986         switch(cfg.xtype) 
44987         {
44988             case 'ContentPanel':  // ContentPanel (el, cfg)
44989             case 'ScrollPanel':  // ContentPanel (el, cfg)
44990                 if(cfg.autoCreate) {
44991                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44992                 } else {
44993                     var el = this.el.createChild();
44994                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44995                 }
44996                 
44997                 this.add(region, ret);
44998                 break;
44999             
45000             
45001             case 'TreePanel': // our new panel!
45002                 cfg.el = this.el.createChild();
45003                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
45004                 this.add(region, ret);
45005                 break;
45006             
45007             case 'NestedLayoutPanel': 
45008                 // create a new Layout (which is  a Border Layout...
45009                 var el = this.el.createChild();
45010                 var clayout = cfg.layout;
45011                 delete cfg.layout;
45012                 clayout.items   = clayout.items  || [];
45013                 // replace this exitems with the clayout ones..
45014                 xitems = clayout.items;
45015                  
45016                 
45017                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
45018                     cfg.background = false;
45019                 }
45020                 var layout = new Roo.BorderLayout(el, clayout);
45021                 
45022                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
45023                 //console.log('adding nested layout panel '  + cfg.toSource());
45024                 this.add(region, ret);
45025                 nb = {}; /// find first...
45026                 break;
45027                 
45028             case 'GridPanel': 
45029             
45030                 // needs grid and region
45031                 
45032                 //var el = this.getRegion(region).el.createChild();
45033                 var el = this.el.createChild();
45034                 // create the grid first...
45035                 
45036                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
45037                 delete cfg.grid;
45038                 if (region == 'center' && this.active ) {
45039                     cfg.background = false;
45040                 }
45041                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
45042                 
45043                 this.add(region, ret);
45044                 if (cfg.background) {
45045                     ret.on('activate', function(gp) {
45046                         if (!gp.grid.rendered) {
45047                             gp.grid.render();
45048                         }
45049                     });
45050                 } else {
45051                     grid.render();
45052                 }
45053                 break;
45054            
45055                
45056                 
45057                 
45058             default: 
45059                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
45060                 return null;
45061              // GridPanel (grid, cfg)
45062             
45063         }
45064         this.beginUpdate();
45065         // add children..
45066         var region = '';
45067         var abn = {};
45068         Roo.each(xitems, function(i)  {
45069             region = nb && i.region ? i.region : false;
45070             
45071             var add = ret.addxtype(i);
45072            
45073             if (region) {
45074                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
45075                 if (!i.background) {
45076                     abn[region] = nb[region] ;
45077                 }
45078             }
45079             
45080         });
45081         this.endUpdate();
45082
45083         // make the last non-background panel active..
45084         //if (nb) { Roo.log(abn); }
45085         if (nb) {
45086             
45087             for(var r in abn) {
45088                 region = this.getRegion(r);
45089                 if (region) {
45090                     // tried using nb[r], but it does not work..
45091                      
45092                     region.showPanel(abn[r]);
45093                    
45094                 }
45095             }
45096         }
45097         return ret;
45098         
45099     }
45100 });
45101
45102 /**
45103  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
45104  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
45105  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
45106  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
45107  * <pre><code>
45108 // shorthand
45109 var CP = Roo.ContentPanel;
45110
45111 var layout = Roo.BorderLayout.create({
45112     north: {
45113         initialSize: 25,
45114         titlebar: false,
45115         panels: [new CP("north", "North")]
45116     },
45117     west: {
45118         split:true,
45119         initialSize: 200,
45120         minSize: 175,
45121         maxSize: 400,
45122         titlebar: true,
45123         collapsible: true,
45124         panels: [new CP("west", {title: "West"})]
45125     },
45126     east: {
45127         split:true,
45128         initialSize: 202,
45129         minSize: 175,
45130         maxSize: 400,
45131         titlebar: true,
45132         collapsible: true,
45133         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
45134     },
45135     south: {
45136         split:true,
45137         initialSize: 100,
45138         minSize: 100,
45139         maxSize: 200,
45140         titlebar: true,
45141         collapsible: true,
45142         panels: [new CP("south", {title: "South", closable: true})]
45143     },
45144     center: {
45145         titlebar: true,
45146         autoScroll:true,
45147         resizeTabs: true,
45148         minTabWidth: 50,
45149         preferredTabWidth: 150,
45150         panels: [
45151             new CP("center1", {title: "Close Me", closable: true}),
45152             new CP("center2", {title: "Center Panel", closable: false})
45153         ]
45154     }
45155 }, document.body);
45156
45157 layout.getRegion("center").showPanel("center1");
45158 </code></pre>
45159  * @param config
45160  * @param targetEl
45161  */
45162 Roo.BorderLayout.create = function(config, targetEl){
45163     var layout = new Roo.BorderLayout(targetEl || document.body, config);
45164     layout.beginUpdate();
45165     var regions = Roo.BorderLayout.RegionFactory.validRegions;
45166     for(var j = 0, jlen = regions.length; j < jlen; j++){
45167         var lr = regions[j];
45168         if(layout.regions[lr] && config[lr].panels){
45169             var r = layout.regions[lr];
45170             var ps = config[lr].panels;
45171             layout.addTypedPanels(r, ps);
45172         }
45173     }
45174     layout.endUpdate();
45175     return layout;
45176 };
45177
45178 // private
45179 Roo.BorderLayout.RegionFactory = {
45180     // private
45181     validRegions : ["north","south","east","west","center"],
45182
45183     // private
45184     create : function(target, mgr, config){
45185         target = target.toLowerCase();
45186         if(config.lightweight || config.basic){
45187             return new Roo.BasicLayoutRegion(mgr, config, target);
45188         }
45189         switch(target){
45190             case "north":
45191                 return new Roo.NorthLayoutRegion(mgr, config);
45192             case "south":
45193                 return new Roo.SouthLayoutRegion(mgr, config);
45194             case "east":
45195                 return new Roo.EastLayoutRegion(mgr, config);
45196             case "west":
45197                 return new Roo.WestLayoutRegion(mgr, config);
45198             case "center":
45199                 return new Roo.CenterLayoutRegion(mgr, config);
45200         }
45201         throw 'Layout region "'+target+'" not supported.';
45202     }
45203 };/*
45204  * Based on:
45205  * Ext JS Library 1.1.1
45206  * Copyright(c) 2006-2007, Ext JS, LLC.
45207  *
45208  * Originally Released Under LGPL - original licence link has changed is not relivant.
45209  *
45210  * Fork - LGPL
45211  * <script type="text/javascript">
45212  */
45213  
45214 /**
45215  * @class Roo.BasicLayoutRegion
45216  * @extends Roo.util.Observable
45217  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45218  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45219  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45220  */
45221 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45222     this.mgr = mgr;
45223     this.position  = pos;
45224     this.events = {
45225         /**
45226          * @scope Roo.BasicLayoutRegion
45227          */
45228         
45229         /**
45230          * @event beforeremove
45231          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45232          * @param {Roo.LayoutRegion} this
45233          * @param {Roo.ContentPanel} panel The panel
45234          * @param {Object} e The cancel event object
45235          */
45236         "beforeremove" : true,
45237         /**
45238          * @event invalidated
45239          * Fires when the layout for this region is changed.
45240          * @param {Roo.LayoutRegion} this
45241          */
45242         "invalidated" : true,
45243         /**
45244          * @event visibilitychange
45245          * Fires when this region is shown or hidden 
45246          * @param {Roo.LayoutRegion} this
45247          * @param {Boolean} visibility true or false
45248          */
45249         "visibilitychange" : true,
45250         /**
45251          * @event paneladded
45252          * Fires when a panel is added. 
45253          * @param {Roo.LayoutRegion} this
45254          * @param {Roo.ContentPanel} panel The panel
45255          */
45256         "paneladded" : true,
45257         /**
45258          * @event panelremoved
45259          * Fires when a panel is removed. 
45260          * @param {Roo.LayoutRegion} this
45261          * @param {Roo.ContentPanel} panel The panel
45262          */
45263         "panelremoved" : true,
45264         /**
45265          * @event collapsed
45266          * Fires when this region is collapsed.
45267          * @param {Roo.LayoutRegion} this
45268          */
45269         "collapsed" : true,
45270         /**
45271          * @event expanded
45272          * Fires when this region is expanded.
45273          * @param {Roo.LayoutRegion} this
45274          */
45275         "expanded" : true,
45276         /**
45277          * @event slideshow
45278          * Fires when this region is slid into view.
45279          * @param {Roo.LayoutRegion} this
45280          */
45281         "slideshow" : true,
45282         /**
45283          * @event slidehide
45284          * Fires when this region slides out of view. 
45285          * @param {Roo.LayoutRegion} this
45286          */
45287         "slidehide" : true,
45288         /**
45289          * @event panelactivated
45290          * Fires when a panel is activated. 
45291          * @param {Roo.LayoutRegion} this
45292          * @param {Roo.ContentPanel} panel The activated panel
45293          */
45294         "panelactivated" : true,
45295         /**
45296          * @event resized
45297          * Fires when the user resizes this region. 
45298          * @param {Roo.LayoutRegion} this
45299          * @param {Number} newSize The new size (width for east/west, height for north/south)
45300          */
45301         "resized" : true
45302     };
45303     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45304     this.panels = new Roo.util.MixedCollection();
45305     this.panels.getKey = this.getPanelId.createDelegate(this);
45306     this.box = null;
45307     this.activePanel = null;
45308     // ensure listeners are added...
45309     
45310     if (config.listeners || config.events) {
45311         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45312             listeners : config.listeners || {},
45313             events : config.events || {}
45314         });
45315     }
45316     
45317     if(skipConfig !== true){
45318         this.applyConfig(config);
45319     }
45320 };
45321
45322 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45323     getPanelId : function(p){
45324         return p.getId();
45325     },
45326     
45327     applyConfig : function(config){
45328         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45329         this.config = config;
45330         
45331     },
45332     
45333     /**
45334      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45335      * the width, for horizontal (north, south) the height.
45336      * @param {Number} newSize The new width or height
45337      */
45338     resizeTo : function(newSize){
45339         var el = this.el ? this.el :
45340                  (this.activePanel ? this.activePanel.getEl() : null);
45341         if(el){
45342             switch(this.position){
45343                 case "east":
45344                 case "west":
45345                     el.setWidth(newSize);
45346                     this.fireEvent("resized", this, newSize);
45347                 break;
45348                 case "north":
45349                 case "south":
45350                     el.setHeight(newSize);
45351                     this.fireEvent("resized", this, newSize);
45352                 break;                
45353             }
45354         }
45355     },
45356     
45357     getBox : function(){
45358         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45359     },
45360     
45361     getMargins : function(){
45362         return this.margins;
45363     },
45364     
45365     updateBox : function(box){
45366         this.box = box;
45367         var el = this.activePanel.getEl();
45368         el.dom.style.left = box.x + "px";
45369         el.dom.style.top = box.y + "px";
45370         this.activePanel.setSize(box.width, box.height);
45371     },
45372     
45373     /**
45374      * Returns the container element for this region.
45375      * @return {Roo.Element}
45376      */
45377     getEl : function(){
45378         return this.activePanel;
45379     },
45380     
45381     /**
45382      * Returns true if this region is currently visible.
45383      * @return {Boolean}
45384      */
45385     isVisible : function(){
45386         return this.activePanel ? true : false;
45387     },
45388     
45389     setActivePanel : function(panel){
45390         panel = this.getPanel(panel);
45391         if(this.activePanel && this.activePanel != panel){
45392             this.activePanel.setActiveState(false);
45393             this.activePanel.getEl().setLeftTop(-10000,-10000);
45394         }
45395         this.activePanel = panel;
45396         panel.setActiveState(true);
45397         if(this.box){
45398             panel.setSize(this.box.width, this.box.height);
45399         }
45400         this.fireEvent("panelactivated", this, panel);
45401         this.fireEvent("invalidated");
45402     },
45403     
45404     /**
45405      * Show the specified panel.
45406      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45407      * @return {Roo.ContentPanel} The shown panel or null
45408      */
45409     showPanel : function(panel){
45410         if(panel = this.getPanel(panel)){
45411             this.setActivePanel(panel);
45412         }
45413         return panel;
45414     },
45415     
45416     /**
45417      * Get the active panel for this region.
45418      * @return {Roo.ContentPanel} The active panel or null
45419      */
45420     getActivePanel : function(){
45421         return this.activePanel;
45422     },
45423     
45424     /**
45425      * Add the passed ContentPanel(s)
45426      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45427      * @return {Roo.ContentPanel} The panel added (if only one was added)
45428      */
45429     add : function(panel){
45430         if(arguments.length > 1){
45431             for(var i = 0, len = arguments.length; i < len; i++) {
45432                 this.add(arguments[i]);
45433             }
45434             return null;
45435         }
45436         if(this.hasPanel(panel)){
45437             this.showPanel(panel);
45438             return panel;
45439         }
45440         var el = panel.getEl();
45441         if(el.dom.parentNode != this.mgr.el.dom){
45442             this.mgr.el.dom.appendChild(el.dom);
45443         }
45444         if(panel.setRegion){
45445             panel.setRegion(this);
45446         }
45447         this.panels.add(panel);
45448         el.setStyle("position", "absolute");
45449         if(!panel.background){
45450             this.setActivePanel(panel);
45451             if(this.config.initialSize && this.panels.getCount()==1){
45452                 this.resizeTo(this.config.initialSize);
45453             }
45454         }
45455         this.fireEvent("paneladded", this, panel);
45456         return panel;
45457     },
45458     
45459     /**
45460      * Returns true if the panel is in this region.
45461      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45462      * @return {Boolean}
45463      */
45464     hasPanel : function(panel){
45465         if(typeof panel == "object"){ // must be panel obj
45466             panel = panel.getId();
45467         }
45468         return this.getPanel(panel) ? true : false;
45469     },
45470     
45471     /**
45472      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45473      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45474      * @param {Boolean} preservePanel Overrides the config preservePanel option
45475      * @return {Roo.ContentPanel} The panel that was removed
45476      */
45477     remove : function(panel, preservePanel){
45478         panel = this.getPanel(panel);
45479         if(!panel){
45480             return null;
45481         }
45482         var e = {};
45483         this.fireEvent("beforeremove", this, panel, e);
45484         if(e.cancel === true){
45485             return null;
45486         }
45487         var panelId = panel.getId();
45488         this.panels.removeKey(panelId);
45489         return panel;
45490     },
45491     
45492     /**
45493      * Returns the panel specified or null if it's not in this region.
45494      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45495      * @return {Roo.ContentPanel}
45496      */
45497     getPanel : function(id){
45498         if(typeof id == "object"){ // must be panel obj
45499             return id;
45500         }
45501         return this.panels.get(id);
45502     },
45503     
45504     /**
45505      * Returns this regions position (north/south/east/west/center).
45506      * @return {String} 
45507      */
45508     getPosition: function(){
45509         return this.position;    
45510     }
45511 });/*
45512  * Based on:
45513  * Ext JS Library 1.1.1
45514  * Copyright(c) 2006-2007, Ext JS, LLC.
45515  *
45516  * Originally Released Under LGPL - original licence link has changed is not relivant.
45517  *
45518  * Fork - LGPL
45519  * <script type="text/javascript">
45520  */
45521  
45522 /**
45523  * @class Roo.LayoutRegion
45524  * @extends Roo.BasicLayoutRegion
45525  * This class represents a region in a layout manager.
45526  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45527  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45528  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45529  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45530  * @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})
45531  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45532  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45533  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45534  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45535  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45536  * @cfg {String}    title           The title for the region (overrides panel titles)
45537  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45538  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45539  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45540  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45541  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45542  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45543  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45544  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45545  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45546  * @cfg {Boolean}   showPin         True to show a pin button
45547  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45548  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45549  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45550  * @cfg {Number}    width           For East/West panels
45551  * @cfg {Number}    height          For North/South panels
45552  * @cfg {Boolean}   split           To show the splitter
45553  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45554  */
45555 Roo.LayoutRegion = function(mgr, config, pos){
45556     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45557     var dh = Roo.DomHelper;
45558     /** This region's container element 
45559     * @type Roo.Element */
45560     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45561     /** This region's title element 
45562     * @type Roo.Element */
45563
45564     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45565         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45566         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45567     ]}, true);
45568     this.titleEl.enableDisplayMode();
45569     /** This region's title text element 
45570     * @type HTMLElement */
45571     this.titleTextEl = this.titleEl.dom.firstChild;
45572     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45573     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45574     this.closeBtn.enableDisplayMode();
45575     this.closeBtn.on("click", this.closeClicked, this);
45576     this.closeBtn.hide();
45577
45578     this.createBody(config);
45579     this.visible = true;
45580     this.collapsed = false;
45581
45582     if(config.hideWhenEmpty){
45583         this.hide();
45584         this.on("paneladded", this.validateVisibility, this);
45585         this.on("panelremoved", this.validateVisibility, this);
45586     }
45587     this.applyConfig(config);
45588 };
45589
45590 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45591
45592     createBody : function(){
45593         /** This region's body element 
45594         * @type Roo.Element */
45595         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45596     },
45597
45598     applyConfig : function(c){
45599         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45600             var dh = Roo.DomHelper;
45601             if(c.titlebar !== false){
45602                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45603                 this.collapseBtn.on("click", this.collapse, this);
45604                 this.collapseBtn.enableDisplayMode();
45605
45606                 if(c.showPin === true || this.showPin){
45607                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45608                     this.stickBtn.enableDisplayMode();
45609                     this.stickBtn.on("click", this.expand, this);
45610                     this.stickBtn.hide();
45611                 }
45612             }
45613             /** This region's collapsed element
45614             * @type Roo.Element */
45615             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45616                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45617             ]}, true);
45618             if(c.floatable !== false){
45619                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45620                this.collapsedEl.on("click", this.collapseClick, this);
45621             }
45622
45623             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45624                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45625                    id: "message", unselectable: "on", style:{"float":"left"}});
45626                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45627              }
45628             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45629             this.expandBtn.on("click", this.expand, this);
45630         }
45631         if(this.collapseBtn){
45632             this.collapseBtn.setVisible(c.collapsible == true);
45633         }
45634         this.cmargins = c.cmargins || this.cmargins ||
45635                          (this.position == "west" || this.position == "east" ?
45636                              {top: 0, left: 2, right:2, bottom: 0} :
45637                              {top: 2, left: 0, right:0, bottom: 2});
45638         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45639         this.bottomTabs = c.tabPosition != "top";
45640         this.autoScroll = c.autoScroll || false;
45641         if(this.autoScroll){
45642             this.bodyEl.setStyle("overflow", "auto");
45643         }else{
45644             this.bodyEl.setStyle("overflow", "hidden");
45645         }
45646         //if(c.titlebar !== false){
45647             if((!c.titlebar && !c.title) || c.titlebar === false){
45648                 this.titleEl.hide();
45649             }else{
45650                 this.titleEl.show();
45651                 if(c.title){
45652                     this.titleTextEl.innerHTML = c.title;
45653                 }
45654             }
45655         //}
45656         this.duration = c.duration || .30;
45657         this.slideDuration = c.slideDuration || .45;
45658         this.config = c;
45659         if(c.collapsed){
45660             this.collapse(true);
45661         }
45662         if(c.hidden){
45663             this.hide();
45664         }
45665     },
45666     /**
45667      * Returns true if this region is currently visible.
45668      * @return {Boolean}
45669      */
45670     isVisible : function(){
45671         return this.visible;
45672     },
45673
45674     /**
45675      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45676      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45677      */
45678     setCollapsedTitle : function(title){
45679         title = title || "&#160;";
45680         if(this.collapsedTitleTextEl){
45681             this.collapsedTitleTextEl.innerHTML = title;
45682         }
45683     },
45684
45685     getBox : function(){
45686         var b;
45687         if(!this.collapsed){
45688             b = this.el.getBox(false, true);
45689         }else{
45690             b = this.collapsedEl.getBox(false, true);
45691         }
45692         return b;
45693     },
45694
45695     getMargins : function(){
45696         return this.collapsed ? this.cmargins : this.margins;
45697     },
45698
45699     highlight : function(){
45700         this.el.addClass("x-layout-panel-dragover");
45701     },
45702
45703     unhighlight : function(){
45704         this.el.removeClass("x-layout-panel-dragover");
45705     },
45706
45707     updateBox : function(box){
45708         this.box = box;
45709         if(!this.collapsed){
45710             this.el.dom.style.left = box.x + "px";
45711             this.el.dom.style.top = box.y + "px";
45712             this.updateBody(box.width, box.height);
45713         }else{
45714             this.collapsedEl.dom.style.left = box.x + "px";
45715             this.collapsedEl.dom.style.top = box.y + "px";
45716             this.collapsedEl.setSize(box.width, box.height);
45717         }
45718         if(this.tabs){
45719             this.tabs.autoSizeTabs();
45720         }
45721     },
45722
45723     updateBody : function(w, h){
45724         if(w !== null){
45725             this.el.setWidth(w);
45726             w -= this.el.getBorderWidth("rl");
45727             if(this.config.adjustments){
45728                 w += this.config.adjustments[0];
45729             }
45730         }
45731         if(h !== null){
45732             this.el.setHeight(h);
45733             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45734             h -= this.el.getBorderWidth("tb");
45735             if(this.config.adjustments){
45736                 h += this.config.adjustments[1];
45737             }
45738             this.bodyEl.setHeight(h);
45739             if(this.tabs){
45740                 h = this.tabs.syncHeight(h);
45741             }
45742         }
45743         if(this.panelSize){
45744             w = w !== null ? w : this.panelSize.width;
45745             h = h !== null ? h : this.panelSize.height;
45746         }
45747         if(this.activePanel){
45748             var el = this.activePanel.getEl();
45749             w = w !== null ? w : el.getWidth();
45750             h = h !== null ? h : el.getHeight();
45751             this.panelSize = {width: w, height: h};
45752             this.activePanel.setSize(w, h);
45753         }
45754         if(Roo.isIE && this.tabs){
45755             this.tabs.el.repaint();
45756         }
45757     },
45758
45759     /**
45760      * Returns the container element for this region.
45761      * @return {Roo.Element}
45762      */
45763     getEl : function(){
45764         return this.el;
45765     },
45766
45767     /**
45768      * Hides this region.
45769      */
45770     hide : function(){
45771         if(!this.collapsed){
45772             this.el.dom.style.left = "-2000px";
45773             this.el.hide();
45774         }else{
45775             this.collapsedEl.dom.style.left = "-2000px";
45776             this.collapsedEl.hide();
45777         }
45778         this.visible = false;
45779         this.fireEvent("visibilitychange", this, false);
45780     },
45781
45782     /**
45783      * Shows this region if it was previously hidden.
45784      */
45785     show : function(){
45786         if(!this.collapsed){
45787             this.el.show();
45788         }else{
45789             this.collapsedEl.show();
45790         }
45791         this.visible = true;
45792         this.fireEvent("visibilitychange", this, true);
45793     },
45794
45795     closeClicked : function(){
45796         if(this.activePanel){
45797             this.remove(this.activePanel);
45798         }
45799     },
45800
45801     collapseClick : function(e){
45802         if(this.isSlid){
45803            e.stopPropagation();
45804            this.slideIn();
45805         }else{
45806            e.stopPropagation();
45807            this.slideOut();
45808         }
45809     },
45810
45811     /**
45812      * Collapses this region.
45813      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45814      */
45815     collapse : function(skipAnim){
45816         if(this.collapsed) return;
45817         this.collapsed = true;
45818         if(this.split){
45819             this.split.el.hide();
45820         }
45821         if(this.config.animate && skipAnim !== true){
45822             this.fireEvent("invalidated", this);
45823             this.animateCollapse();
45824         }else{
45825             this.el.setLocation(-20000,-20000);
45826             this.el.hide();
45827             this.collapsedEl.show();
45828             this.fireEvent("collapsed", this);
45829             this.fireEvent("invalidated", this);
45830         }
45831     },
45832
45833     animateCollapse : function(){
45834         // overridden
45835     },
45836
45837     /**
45838      * Expands this region if it was previously collapsed.
45839      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45840      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45841      */
45842     expand : function(e, skipAnim){
45843         if(e) e.stopPropagation();
45844         if(!this.collapsed || this.el.hasActiveFx()) return;
45845         if(this.isSlid){
45846             this.afterSlideIn();
45847             skipAnim = true;
45848         }
45849         this.collapsed = false;
45850         if(this.config.animate && skipAnim !== true){
45851             this.animateExpand();
45852         }else{
45853             this.el.show();
45854             if(this.split){
45855                 this.split.el.show();
45856             }
45857             this.collapsedEl.setLocation(-2000,-2000);
45858             this.collapsedEl.hide();
45859             this.fireEvent("invalidated", this);
45860             this.fireEvent("expanded", this);
45861         }
45862     },
45863
45864     animateExpand : function(){
45865         // overridden
45866     },
45867
45868     initTabs : function()
45869     {
45870         this.bodyEl.setStyle("overflow", "hidden");
45871         var ts = new Roo.TabPanel(
45872                 this.bodyEl.dom,
45873                 {
45874                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45875                     disableTooltips: this.config.disableTabTips,
45876                     toolbar : this.config.toolbar
45877                 }
45878         );
45879         if(this.config.hideTabs){
45880             ts.stripWrap.setDisplayed(false);
45881         }
45882         this.tabs = ts;
45883         ts.resizeTabs = this.config.resizeTabs === true;
45884         ts.minTabWidth = this.config.minTabWidth || 40;
45885         ts.maxTabWidth = this.config.maxTabWidth || 250;
45886         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45887         ts.monitorResize = false;
45888         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45889         ts.bodyEl.addClass('x-layout-tabs-body');
45890         this.panels.each(this.initPanelAsTab, this);
45891     },
45892
45893     initPanelAsTab : function(panel){
45894         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45895                     this.config.closeOnTab && panel.isClosable());
45896         if(panel.tabTip !== undefined){
45897             ti.setTooltip(panel.tabTip);
45898         }
45899         ti.on("activate", function(){
45900               this.setActivePanel(panel);
45901         }, this);
45902         if(this.config.closeOnTab){
45903             ti.on("beforeclose", function(t, e){
45904                 e.cancel = true;
45905                 this.remove(panel);
45906             }, this);
45907         }
45908         return ti;
45909     },
45910
45911     updatePanelTitle : function(panel, title){
45912         if(this.activePanel == panel){
45913             this.updateTitle(title);
45914         }
45915         if(this.tabs){
45916             var ti = this.tabs.getTab(panel.getEl().id);
45917             ti.setText(title);
45918             if(panel.tabTip !== undefined){
45919                 ti.setTooltip(panel.tabTip);
45920             }
45921         }
45922     },
45923
45924     updateTitle : function(title){
45925         if(this.titleTextEl && !this.config.title){
45926             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45927         }
45928     },
45929
45930     setActivePanel : function(panel){
45931         panel = this.getPanel(panel);
45932         if(this.activePanel && this.activePanel != panel){
45933             this.activePanel.setActiveState(false);
45934         }
45935         this.activePanel = panel;
45936         panel.setActiveState(true);
45937         if(this.panelSize){
45938             panel.setSize(this.panelSize.width, this.panelSize.height);
45939         }
45940         if(this.closeBtn){
45941             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45942         }
45943         this.updateTitle(panel.getTitle());
45944         if(this.tabs){
45945             this.fireEvent("invalidated", this);
45946         }
45947         this.fireEvent("panelactivated", this, panel);
45948     },
45949
45950     /**
45951      * Shows the specified panel.
45952      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45953      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45954      */
45955     showPanel : function(panel){
45956         if(panel = this.getPanel(panel)){
45957             if(this.tabs){
45958                 var tab = this.tabs.getTab(panel.getEl().id);
45959                 if(tab.isHidden()){
45960                     this.tabs.unhideTab(tab.id);
45961                 }
45962                 tab.activate();
45963             }else{
45964                 this.setActivePanel(panel);
45965             }
45966         }
45967         return panel;
45968     },
45969
45970     /**
45971      * Get the active panel for this region.
45972      * @return {Roo.ContentPanel} The active panel or null
45973      */
45974     getActivePanel : function(){
45975         return this.activePanel;
45976     },
45977
45978     validateVisibility : function(){
45979         if(this.panels.getCount() < 1){
45980             this.updateTitle("&#160;");
45981             this.closeBtn.hide();
45982             this.hide();
45983         }else{
45984             if(!this.isVisible()){
45985                 this.show();
45986             }
45987         }
45988     },
45989
45990     /**
45991      * Adds the passed ContentPanel(s) to this region.
45992      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45993      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45994      */
45995     add : function(panel){
45996         if(arguments.length > 1){
45997             for(var i = 0, len = arguments.length; i < len; i++) {
45998                 this.add(arguments[i]);
45999             }
46000             return null;
46001         }
46002         if(this.hasPanel(panel)){
46003             this.showPanel(panel);
46004             return panel;
46005         }
46006         panel.setRegion(this);
46007         this.panels.add(panel);
46008         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
46009             this.bodyEl.dom.appendChild(panel.getEl().dom);
46010             if(panel.background !== true){
46011                 this.setActivePanel(panel);
46012             }
46013             this.fireEvent("paneladded", this, panel);
46014             return panel;
46015         }
46016         if(!this.tabs){
46017             this.initTabs();
46018         }else{
46019             this.initPanelAsTab(panel);
46020         }
46021         if(panel.background !== true){
46022             this.tabs.activate(panel.getEl().id);
46023         }
46024         this.fireEvent("paneladded", this, panel);
46025         return panel;
46026     },
46027
46028     /**
46029      * Hides the tab for the specified panel.
46030      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46031      */
46032     hidePanel : function(panel){
46033         if(this.tabs && (panel = this.getPanel(panel))){
46034             this.tabs.hideTab(panel.getEl().id);
46035         }
46036     },
46037
46038     /**
46039      * Unhides the tab for a previously hidden panel.
46040      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46041      */
46042     unhidePanel : function(panel){
46043         if(this.tabs && (panel = this.getPanel(panel))){
46044             this.tabs.unhideTab(panel.getEl().id);
46045         }
46046     },
46047
46048     clearPanels : function(){
46049         while(this.panels.getCount() > 0){
46050              this.remove(this.panels.first());
46051         }
46052     },
46053
46054     /**
46055      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46056      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46057      * @param {Boolean} preservePanel Overrides the config preservePanel option
46058      * @return {Roo.ContentPanel} The panel that was removed
46059      */
46060     remove : function(panel, preservePanel){
46061         panel = this.getPanel(panel);
46062         if(!panel){
46063             return null;
46064         }
46065         var e = {};
46066         this.fireEvent("beforeremove", this, panel, e);
46067         if(e.cancel === true){
46068             return null;
46069         }
46070         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
46071         var panelId = panel.getId();
46072         this.panels.removeKey(panelId);
46073         if(preservePanel){
46074             document.body.appendChild(panel.getEl().dom);
46075         }
46076         if(this.tabs){
46077             this.tabs.removeTab(panel.getEl().id);
46078         }else if (!preservePanel){
46079             this.bodyEl.dom.removeChild(panel.getEl().dom);
46080         }
46081         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
46082             var p = this.panels.first();
46083             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
46084             tempEl.appendChild(p.getEl().dom);
46085             this.bodyEl.update("");
46086             this.bodyEl.dom.appendChild(p.getEl().dom);
46087             tempEl = null;
46088             this.updateTitle(p.getTitle());
46089             this.tabs = null;
46090             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46091             this.setActivePanel(p);
46092         }
46093         panel.setRegion(null);
46094         if(this.activePanel == panel){
46095             this.activePanel = null;
46096         }
46097         if(this.config.autoDestroy !== false && preservePanel !== true){
46098             try{panel.destroy();}catch(e){}
46099         }
46100         this.fireEvent("panelremoved", this, panel);
46101         return panel;
46102     },
46103
46104     /**
46105      * Returns the TabPanel component used by this region
46106      * @return {Roo.TabPanel}
46107      */
46108     getTabs : function(){
46109         return this.tabs;
46110     },
46111
46112     createTool : function(parentEl, className){
46113         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
46114             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
46115         btn.addClassOnOver("x-layout-tools-button-over");
46116         return btn;
46117     }
46118 });/*
46119  * Based on:
46120  * Ext JS Library 1.1.1
46121  * Copyright(c) 2006-2007, Ext JS, LLC.
46122  *
46123  * Originally Released Under LGPL - original licence link has changed is not relivant.
46124  *
46125  * Fork - LGPL
46126  * <script type="text/javascript">
46127  */
46128  
46129
46130
46131 /**
46132  * @class Roo.SplitLayoutRegion
46133  * @extends Roo.LayoutRegion
46134  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
46135  */
46136 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
46137     this.cursor = cursor;
46138     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
46139 };
46140
46141 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
46142     splitTip : "Drag to resize.",
46143     collapsibleSplitTip : "Drag to resize. Double click to hide.",
46144     useSplitTips : false,
46145
46146     applyConfig : function(config){
46147         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
46148         if(config.split){
46149             if(!this.split){
46150                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
46151                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
46152                 /** The SplitBar for this region 
46153                 * @type Roo.SplitBar */
46154                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
46155                 this.split.on("moved", this.onSplitMove, this);
46156                 this.split.useShim = config.useShim === true;
46157                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
46158                 if(this.useSplitTips){
46159                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
46160                 }
46161                 if(config.collapsible){
46162                     this.split.el.on("dblclick", this.collapse,  this);
46163                 }
46164             }
46165             if(typeof config.minSize != "undefined"){
46166                 this.split.minSize = config.minSize;
46167             }
46168             if(typeof config.maxSize != "undefined"){
46169                 this.split.maxSize = config.maxSize;
46170             }
46171             if(config.hideWhenEmpty || config.hidden || config.collapsed){
46172                 this.hideSplitter();
46173             }
46174         }
46175     },
46176
46177     getHMaxSize : function(){
46178          var cmax = this.config.maxSize || 10000;
46179          var center = this.mgr.getRegion("center");
46180          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46181     },
46182
46183     getVMaxSize : function(){
46184          var cmax = this.config.maxSize || 10000;
46185          var center = this.mgr.getRegion("center");
46186          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46187     },
46188
46189     onSplitMove : function(split, newSize){
46190         this.fireEvent("resized", this, newSize);
46191     },
46192     
46193     /** 
46194      * Returns the {@link Roo.SplitBar} for this region.
46195      * @return {Roo.SplitBar}
46196      */
46197     getSplitBar : function(){
46198         return this.split;
46199     },
46200     
46201     hide : function(){
46202         this.hideSplitter();
46203         Roo.SplitLayoutRegion.superclass.hide.call(this);
46204     },
46205
46206     hideSplitter : function(){
46207         if(this.split){
46208             this.split.el.setLocation(-2000,-2000);
46209             this.split.el.hide();
46210         }
46211     },
46212
46213     show : function(){
46214         if(this.split){
46215             this.split.el.show();
46216         }
46217         Roo.SplitLayoutRegion.superclass.show.call(this);
46218     },
46219     
46220     beforeSlide: function(){
46221         if(Roo.isGecko){// firefox overflow auto bug workaround
46222             this.bodyEl.clip();
46223             if(this.tabs) this.tabs.bodyEl.clip();
46224             if(this.activePanel){
46225                 this.activePanel.getEl().clip();
46226                 
46227                 if(this.activePanel.beforeSlide){
46228                     this.activePanel.beforeSlide();
46229                 }
46230             }
46231         }
46232     },
46233     
46234     afterSlide : function(){
46235         if(Roo.isGecko){// firefox overflow auto bug workaround
46236             this.bodyEl.unclip();
46237             if(this.tabs) this.tabs.bodyEl.unclip();
46238             if(this.activePanel){
46239                 this.activePanel.getEl().unclip();
46240                 if(this.activePanel.afterSlide){
46241                     this.activePanel.afterSlide();
46242                 }
46243             }
46244         }
46245     },
46246
46247     initAutoHide : function(){
46248         if(this.autoHide !== false){
46249             if(!this.autoHideHd){
46250                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46251                 this.autoHideHd = {
46252                     "mouseout": function(e){
46253                         if(!e.within(this.el, true)){
46254                             st.delay(500);
46255                         }
46256                     },
46257                     "mouseover" : function(e){
46258                         st.cancel();
46259                     },
46260                     scope : this
46261                 };
46262             }
46263             this.el.on(this.autoHideHd);
46264         }
46265     },
46266
46267     clearAutoHide : function(){
46268         if(this.autoHide !== false){
46269             this.el.un("mouseout", this.autoHideHd.mouseout);
46270             this.el.un("mouseover", this.autoHideHd.mouseover);
46271         }
46272     },
46273
46274     clearMonitor : function(){
46275         Roo.get(document).un("click", this.slideInIf, this);
46276     },
46277
46278     // these names are backwards but not changed for compat
46279     slideOut : function(){
46280         if(this.isSlid || this.el.hasActiveFx()){
46281             return;
46282         }
46283         this.isSlid = true;
46284         if(this.collapseBtn){
46285             this.collapseBtn.hide();
46286         }
46287         this.closeBtnState = this.closeBtn.getStyle('display');
46288         this.closeBtn.hide();
46289         if(this.stickBtn){
46290             this.stickBtn.show();
46291         }
46292         this.el.show();
46293         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46294         this.beforeSlide();
46295         this.el.setStyle("z-index", 10001);
46296         this.el.slideIn(this.getSlideAnchor(), {
46297             callback: function(){
46298                 this.afterSlide();
46299                 this.initAutoHide();
46300                 Roo.get(document).on("click", this.slideInIf, this);
46301                 this.fireEvent("slideshow", this);
46302             },
46303             scope: this,
46304             block: true
46305         });
46306     },
46307
46308     afterSlideIn : function(){
46309         this.clearAutoHide();
46310         this.isSlid = false;
46311         this.clearMonitor();
46312         this.el.setStyle("z-index", "");
46313         if(this.collapseBtn){
46314             this.collapseBtn.show();
46315         }
46316         this.closeBtn.setStyle('display', this.closeBtnState);
46317         if(this.stickBtn){
46318             this.stickBtn.hide();
46319         }
46320         this.fireEvent("slidehide", this);
46321     },
46322
46323     slideIn : function(cb){
46324         if(!this.isSlid || this.el.hasActiveFx()){
46325             Roo.callback(cb);
46326             return;
46327         }
46328         this.isSlid = false;
46329         this.beforeSlide();
46330         this.el.slideOut(this.getSlideAnchor(), {
46331             callback: function(){
46332                 this.el.setLeftTop(-10000, -10000);
46333                 this.afterSlide();
46334                 this.afterSlideIn();
46335                 Roo.callback(cb);
46336             },
46337             scope: this,
46338             block: true
46339         });
46340     },
46341     
46342     slideInIf : function(e){
46343         if(!e.within(this.el)){
46344             this.slideIn();
46345         }
46346     },
46347
46348     animateCollapse : function(){
46349         this.beforeSlide();
46350         this.el.setStyle("z-index", 20000);
46351         var anchor = this.getSlideAnchor();
46352         this.el.slideOut(anchor, {
46353             callback : function(){
46354                 this.el.setStyle("z-index", "");
46355                 this.collapsedEl.slideIn(anchor, {duration:.3});
46356                 this.afterSlide();
46357                 this.el.setLocation(-10000,-10000);
46358                 this.el.hide();
46359                 this.fireEvent("collapsed", this);
46360             },
46361             scope: this,
46362             block: true
46363         });
46364     },
46365
46366     animateExpand : function(){
46367         this.beforeSlide();
46368         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46369         this.el.setStyle("z-index", 20000);
46370         this.collapsedEl.hide({
46371             duration:.1
46372         });
46373         this.el.slideIn(this.getSlideAnchor(), {
46374             callback : function(){
46375                 this.el.setStyle("z-index", "");
46376                 this.afterSlide();
46377                 if(this.split){
46378                     this.split.el.show();
46379                 }
46380                 this.fireEvent("invalidated", this);
46381                 this.fireEvent("expanded", this);
46382             },
46383             scope: this,
46384             block: true
46385         });
46386     },
46387
46388     anchors : {
46389         "west" : "left",
46390         "east" : "right",
46391         "north" : "top",
46392         "south" : "bottom"
46393     },
46394
46395     sanchors : {
46396         "west" : "l",
46397         "east" : "r",
46398         "north" : "t",
46399         "south" : "b"
46400     },
46401
46402     canchors : {
46403         "west" : "tl-tr",
46404         "east" : "tr-tl",
46405         "north" : "tl-bl",
46406         "south" : "bl-tl"
46407     },
46408
46409     getAnchor : function(){
46410         return this.anchors[this.position];
46411     },
46412
46413     getCollapseAnchor : function(){
46414         return this.canchors[this.position];
46415     },
46416
46417     getSlideAnchor : function(){
46418         return this.sanchors[this.position];
46419     },
46420
46421     getAlignAdj : function(){
46422         var cm = this.cmargins;
46423         switch(this.position){
46424             case "west":
46425                 return [0, 0];
46426             break;
46427             case "east":
46428                 return [0, 0];
46429             break;
46430             case "north":
46431                 return [0, 0];
46432             break;
46433             case "south":
46434                 return [0, 0];
46435             break;
46436         }
46437     },
46438
46439     getExpandAdj : function(){
46440         var c = this.collapsedEl, cm = this.cmargins;
46441         switch(this.position){
46442             case "west":
46443                 return [-(cm.right+c.getWidth()+cm.left), 0];
46444             break;
46445             case "east":
46446                 return [cm.right+c.getWidth()+cm.left, 0];
46447             break;
46448             case "north":
46449                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46450             break;
46451             case "south":
46452                 return [0, cm.top+cm.bottom+c.getHeight()];
46453             break;
46454         }
46455     }
46456 });/*
46457  * Based on:
46458  * Ext JS Library 1.1.1
46459  * Copyright(c) 2006-2007, Ext JS, LLC.
46460  *
46461  * Originally Released Under LGPL - original licence link has changed is not relivant.
46462  *
46463  * Fork - LGPL
46464  * <script type="text/javascript">
46465  */
46466 /*
46467  * These classes are private internal classes
46468  */
46469 Roo.CenterLayoutRegion = function(mgr, config){
46470     Roo.LayoutRegion.call(this, mgr, config, "center");
46471     this.visible = true;
46472     this.minWidth = config.minWidth || 20;
46473     this.minHeight = config.minHeight || 20;
46474 };
46475
46476 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46477     hide : function(){
46478         // center panel can't be hidden
46479     },
46480     
46481     show : function(){
46482         // center panel can't be hidden
46483     },
46484     
46485     getMinWidth: function(){
46486         return this.minWidth;
46487     },
46488     
46489     getMinHeight: function(){
46490         return this.minHeight;
46491     }
46492 });
46493
46494
46495 Roo.NorthLayoutRegion = function(mgr, config){
46496     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46497     if(this.split){
46498         this.split.placement = Roo.SplitBar.TOP;
46499         this.split.orientation = Roo.SplitBar.VERTICAL;
46500         this.split.el.addClass("x-layout-split-v");
46501     }
46502     var size = config.initialSize || config.height;
46503     if(typeof size != "undefined"){
46504         this.el.setHeight(size);
46505     }
46506 };
46507 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46508     orientation: Roo.SplitBar.VERTICAL,
46509     getBox : function(){
46510         if(this.collapsed){
46511             return this.collapsedEl.getBox();
46512         }
46513         var box = this.el.getBox();
46514         if(this.split){
46515             box.height += this.split.el.getHeight();
46516         }
46517         return box;
46518     },
46519     
46520     updateBox : function(box){
46521         if(this.split && !this.collapsed){
46522             box.height -= this.split.el.getHeight();
46523             this.split.el.setLeft(box.x);
46524             this.split.el.setTop(box.y+box.height);
46525             this.split.el.setWidth(box.width);
46526         }
46527         if(this.collapsed){
46528             this.updateBody(box.width, null);
46529         }
46530         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46531     }
46532 });
46533
46534 Roo.SouthLayoutRegion = function(mgr, config){
46535     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46536     if(this.split){
46537         this.split.placement = Roo.SplitBar.BOTTOM;
46538         this.split.orientation = Roo.SplitBar.VERTICAL;
46539         this.split.el.addClass("x-layout-split-v");
46540     }
46541     var size = config.initialSize || config.height;
46542     if(typeof size != "undefined"){
46543         this.el.setHeight(size);
46544     }
46545 };
46546 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46547     orientation: Roo.SplitBar.VERTICAL,
46548     getBox : function(){
46549         if(this.collapsed){
46550             return this.collapsedEl.getBox();
46551         }
46552         var box = this.el.getBox();
46553         if(this.split){
46554             var sh = this.split.el.getHeight();
46555             box.height += sh;
46556             box.y -= sh;
46557         }
46558         return box;
46559     },
46560     
46561     updateBox : function(box){
46562         if(this.split && !this.collapsed){
46563             var sh = this.split.el.getHeight();
46564             box.height -= sh;
46565             box.y += sh;
46566             this.split.el.setLeft(box.x);
46567             this.split.el.setTop(box.y-sh);
46568             this.split.el.setWidth(box.width);
46569         }
46570         if(this.collapsed){
46571             this.updateBody(box.width, null);
46572         }
46573         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46574     }
46575 });
46576
46577 Roo.EastLayoutRegion = function(mgr, config){
46578     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46579     if(this.split){
46580         this.split.placement = Roo.SplitBar.RIGHT;
46581         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46582         this.split.el.addClass("x-layout-split-h");
46583     }
46584     var size = config.initialSize || config.width;
46585     if(typeof size != "undefined"){
46586         this.el.setWidth(size);
46587     }
46588 };
46589 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46590     orientation: Roo.SplitBar.HORIZONTAL,
46591     getBox : function(){
46592         if(this.collapsed){
46593             return this.collapsedEl.getBox();
46594         }
46595         var box = this.el.getBox();
46596         if(this.split){
46597             var sw = this.split.el.getWidth();
46598             box.width += sw;
46599             box.x -= sw;
46600         }
46601         return box;
46602     },
46603
46604     updateBox : function(box){
46605         if(this.split && !this.collapsed){
46606             var sw = this.split.el.getWidth();
46607             box.width -= sw;
46608             this.split.el.setLeft(box.x);
46609             this.split.el.setTop(box.y);
46610             this.split.el.setHeight(box.height);
46611             box.x += sw;
46612         }
46613         if(this.collapsed){
46614             this.updateBody(null, box.height);
46615         }
46616         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46617     }
46618 });
46619
46620 Roo.WestLayoutRegion = function(mgr, config){
46621     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46622     if(this.split){
46623         this.split.placement = Roo.SplitBar.LEFT;
46624         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46625         this.split.el.addClass("x-layout-split-h");
46626     }
46627     var size = config.initialSize || config.width;
46628     if(typeof size != "undefined"){
46629         this.el.setWidth(size);
46630     }
46631 };
46632 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46633     orientation: Roo.SplitBar.HORIZONTAL,
46634     getBox : function(){
46635         if(this.collapsed){
46636             return this.collapsedEl.getBox();
46637         }
46638         var box = this.el.getBox();
46639         if(this.split){
46640             box.width += this.split.el.getWidth();
46641         }
46642         return box;
46643     },
46644     
46645     updateBox : function(box){
46646         if(this.split && !this.collapsed){
46647             var sw = this.split.el.getWidth();
46648             box.width -= sw;
46649             this.split.el.setLeft(box.x+box.width);
46650             this.split.el.setTop(box.y);
46651             this.split.el.setHeight(box.height);
46652         }
46653         if(this.collapsed){
46654             this.updateBody(null, box.height);
46655         }
46656         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46657     }
46658 });
46659 /*
46660  * Based on:
46661  * Ext JS Library 1.1.1
46662  * Copyright(c) 2006-2007, Ext JS, LLC.
46663  *
46664  * Originally Released Under LGPL - original licence link has changed is not relivant.
46665  *
46666  * Fork - LGPL
46667  * <script type="text/javascript">
46668  */
46669  
46670  
46671 /*
46672  * Private internal class for reading and applying state
46673  */
46674 Roo.LayoutStateManager = function(layout){
46675      // default empty state
46676      this.state = {
46677         north: {},
46678         south: {},
46679         east: {},
46680         west: {}       
46681     };
46682 };
46683
46684 Roo.LayoutStateManager.prototype = {
46685     init : function(layout, provider){
46686         this.provider = provider;
46687         var state = provider.get(layout.id+"-layout-state");
46688         if(state){
46689             var wasUpdating = layout.isUpdating();
46690             if(!wasUpdating){
46691                 layout.beginUpdate();
46692             }
46693             for(var key in state){
46694                 if(typeof state[key] != "function"){
46695                     var rstate = state[key];
46696                     var r = layout.getRegion(key);
46697                     if(r && rstate){
46698                         if(rstate.size){
46699                             r.resizeTo(rstate.size);
46700                         }
46701                         if(rstate.collapsed == true){
46702                             r.collapse(true);
46703                         }else{
46704                             r.expand(null, true);
46705                         }
46706                     }
46707                 }
46708             }
46709             if(!wasUpdating){
46710                 layout.endUpdate();
46711             }
46712             this.state = state; 
46713         }
46714         this.layout = layout;
46715         layout.on("regionresized", this.onRegionResized, this);
46716         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46717         layout.on("regionexpanded", this.onRegionExpanded, this);
46718     },
46719     
46720     storeState : function(){
46721         this.provider.set(this.layout.id+"-layout-state", this.state);
46722     },
46723     
46724     onRegionResized : function(region, newSize){
46725         this.state[region.getPosition()].size = newSize;
46726         this.storeState();
46727     },
46728     
46729     onRegionCollapsed : function(region){
46730         this.state[region.getPosition()].collapsed = true;
46731         this.storeState();
46732     },
46733     
46734     onRegionExpanded : function(region){
46735         this.state[region.getPosition()].collapsed = false;
46736         this.storeState();
46737     }
46738 };/*
46739  * Based on:
46740  * Ext JS Library 1.1.1
46741  * Copyright(c) 2006-2007, Ext JS, LLC.
46742  *
46743  * Originally Released Under LGPL - original licence link has changed is not relivant.
46744  *
46745  * Fork - LGPL
46746  * <script type="text/javascript">
46747  */
46748 /**
46749  * @class Roo.ContentPanel
46750  * @extends Roo.util.Observable
46751  * A basic ContentPanel element.
46752  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46753  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46754  * @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
46755  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46756  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46757  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46758  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46759  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46760  * @cfg {String} title          The title for this panel
46761  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46762  * @cfg {String} url            Calls {@link #setUrl} with this value
46763  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46764  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46765  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46766  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46767
46768  * @constructor
46769  * Create a new ContentPanel.
46770  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46771  * @param {String/Object} config A string to set only the title or a config object
46772  * @param {String} content (optional) Set the HTML content for this panel
46773  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46774  */
46775 Roo.ContentPanel = function(el, config, content){
46776     
46777      
46778     /*
46779     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46780         config = el;
46781         el = Roo.id();
46782     }
46783     if (config && config.parentLayout) { 
46784         el = config.parentLayout.el.createChild(); 
46785     }
46786     */
46787     if(el.autoCreate){ // xtype is available if this is called from factory
46788         config = el;
46789         el = Roo.id();
46790     }
46791     this.el = Roo.get(el);
46792     if(!this.el && config && config.autoCreate){
46793         if(typeof config.autoCreate == "object"){
46794             if(!config.autoCreate.id){
46795                 config.autoCreate.id = config.id||el;
46796             }
46797             this.el = Roo.DomHelper.append(document.body,
46798                         config.autoCreate, true);
46799         }else{
46800             this.el = Roo.DomHelper.append(document.body,
46801                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46802         }
46803     }
46804     this.closable = false;
46805     this.loaded = false;
46806     this.active = false;
46807     if(typeof config == "string"){
46808         this.title = config;
46809     }else{
46810         Roo.apply(this, config);
46811     }
46812     
46813     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46814         this.wrapEl = this.el.wrap();
46815         this.toolbar.container = this.el.insertSibling(false, 'before');
46816         this.toolbar = new Roo.Toolbar(this.toolbar);
46817     }
46818     
46819     
46820     
46821     if(this.resizeEl){
46822         this.resizeEl = Roo.get(this.resizeEl, true);
46823     }else{
46824         this.resizeEl = this.el;
46825     }
46826     this.addEvents({
46827         /**
46828          * @event activate
46829          * Fires when this panel is activated. 
46830          * @param {Roo.ContentPanel} this
46831          */
46832         "activate" : true,
46833         /**
46834          * @event deactivate
46835          * Fires when this panel is activated. 
46836          * @param {Roo.ContentPanel} this
46837          */
46838         "deactivate" : true,
46839
46840         /**
46841          * @event resize
46842          * Fires when this panel is resized if fitToFrame is true.
46843          * @param {Roo.ContentPanel} this
46844          * @param {Number} width The width after any component adjustments
46845          * @param {Number} height The height after any component adjustments
46846          */
46847         "resize" : true,
46848         
46849          /**
46850          * @event render
46851          * Fires when this tab is created
46852          * @param {Roo.ContentPanel} this
46853          */
46854         "render" : true
46855         
46856         
46857         
46858     });
46859     if(this.autoScroll){
46860         this.resizeEl.setStyle("overflow", "auto");
46861     } else {
46862         // fix randome scrolling
46863         this.el.on('scroll', function() {
46864             Roo.log('fix random scolling');
46865             this.scrollTo('top',0); 
46866         });
46867     }
46868     content = content || this.content;
46869     if(content){
46870         this.setContent(content);
46871     }
46872     if(config && config.url){
46873         this.setUrl(this.url, this.params, this.loadOnce);
46874     }
46875     
46876     
46877     
46878     Roo.ContentPanel.superclass.constructor.call(this);
46879     
46880     this.fireEvent('render', this);
46881 };
46882
46883 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46884     tabTip:'',
46885     setRegion : function(region){
46886         this.region = region;
46887         if(region){
46888            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46889         }else{
46890            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46891         } 
46892     },
46893     
46894     /**
46895      * Returns the toolbar for this Panel if one was configured. 
46896      * @return {Roo.Toolbar} 
46897      */
46898     getToolbar : function(){
46899         return this.toolbar;
46900     },
46901     
46902     setActiveState : function(active){
46903         this.active = active;
46904         if(!active){
46905             this.fireEvent("deactivate", this);
46906         }else{
46907             this.fireEvent("activate", this);
46908         }
46909     },
46910     /**
46911      * Updates this panel's element
46912      * @param {String} content The new content
46913      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46914     */
46915     setContent : function(content, loadScripts){
46916         this.el.update(content, loadScripts);
46917     },
46918
46919     ignoreResize : function(w, h){
46920         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46921             return true;
46922         }else{
46923             this.lastSize = {width: w, height: h};
46924             return false;
46925         }
46926     },
46927     /**
46928      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46929      * @return {Roo.UpdateManager} The UpdateManager
46930      */
46931     getUpdateManager : function(){
46932         return this.el.getUpdateManager();
46933     },
46934      /**
46935      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46936      * @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:
46937 <pre><code>
46938 panel.load({
46939     url: "your-url.php",
46940     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46941     callback: yourFunction,
46942     scope: yourObject, //(optional scope)
46943     discardUrl: false,
46944     nocache: false,
46945     text: "Loading...",
46946     timeout: 30,
46947     scripts: false
46948 });
46949 </code></pre>
46950      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46951      * 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.
46952      * @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}
46953      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46954      * @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.
46955      * @return {Roo.ContentPanel} this
46956      */
46957     load : function(){
46958         var um = this.el.getUpdateManager();
46959         um.update.apply(um, arguments);
46960         return this;
46961     },
46962
46963
46964     /**
46965      * 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.
46966      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46967      * @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)
46968      * @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)
46969      * @return {Roo.UpdateManager} The UpdateManager
46970      */
46971     setUrl : function(url, params, loadOnce){
46972         if(this.refreshDelegate){
46973             this.removeListener("activate", this.refreshDelegate);
46974         }
46975         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46976         this.on("activate", this.refreshDelegate);
46977         return this.el.getUpdateManager();
46978     },
46979     
46980     _handleRefresh : function(url, params, loadOnce){
46981         if(!loadOnce || !this.loaded){
46982             var updater = this.el.getUpdateManager();
46983             updater.update(url, params, this._setLoaded.createDelegate(this));
46984         }
46985     },
46986     
46987     _setLoaded : function(){
46988         this.loaded = true;
46989     }, 
46990     
46991     /**
46992      * Returns this panel's id
46993      * @return {String} 
46994      */
46995     getId : function(){
46996         return this.el.id;
46997     },
46998     
46999     /** 
47000      * Returns this panel's element - used by regiosn to add.
47001      * @return {Roo.Element} 
47002      */
47003     getEl : function(){
47004         return this.wrapEl || this.el;
47005     },
47006     
47007     adjustForComponents : function(width, height){
47008         if(this.resizeEl != this.el){
47009             width -= this.el.getFrameWidth('lr');
47010             height -= this.el.getFrameWidth('tb');
47011         }
47012         if(this.toolbar){
47013             var te = this.toolbar.getEl();
47014             height -= te.getHeight();
47015             te.setWidth(width);
47016         }
47017         if(this.adjustments){
47018             width += this.adjustments[0];
47019             height += this.adjustments[1];
47020         }
47021         return {"width": width, "height": height};
47022     },
47023     
47024     setSize : function(width, height){
47025         if(this.fitToFrame && !this.ignoreResize(width, height)){
47026             if(this.fitContainer && this.resizeEl != this.el){
47027                 this.el.setSize(width, height);
47028             }
47029             var size = this.adjustForComponents(width, height);
47030             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
47031             this.fireEvent('resize', this, size.width, size.height);
47032         }
47033     },
47034     
47035     /**
47036      * Returns this panel's title
47037      * @return {String} 
47038      */
47039     getTitle : function(){
47040         return this.title;
47041     },
47042     
47043     /**
47044      * Set this panel's title
47045      * @param {String} title
47046      */
47047     setTitle : function(title){
47048         this.title = title;
47049         if(this.region){
47050             this.region.updatePanelTitle(this, title);
47051         }
47052     },
47053     
47054     /**
47055      * Returns true is this panel was configured to be closable
47056      * @return {Boolean} 
47057      */
47058     isClosable : function(){
47059         return this.closable;
47060     },
47061     
47062     beforeSlide : function(){
47063         this.el.clip();
47064         this.resizeEl.clip();
47065     },
47066     
47067     afterSlide : function(){
47068         this.el.unclip();
47069         this.resizeEl.unclip();
47070     },
47071     
47072     /**
47073      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
47074      *   Will fail silently if the {@link #setUrl} method has not been called.
47075      *   This does not activate the panel, just updates its content.
47076      */
47077     refresh : function(){
47078         if(this.refreshDelegate){
47079            this.loaded = false;
47080            this.refreshDelegate();
47081         }
47082     },
47083     
47084     /**
47085      * Destroys this panel
47086      */
47087     destroy : function(){
47088         this.el.removeAllListeners();
47089         var tempEl = document.createElement("span");
47090         tempEl.appendChild(this.el.dom);
47091         tempEl.innerHTML = "";
47092         this.el.remove();
47093         this.el = null;
47094     },
47095     
47096     /**
47097      * form - if the content panel contains a form - this is a reference to it.
47098      * @type {Roo.form.Form}
47099      */
47100     form : false,
47101     /**
47102      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
47103      *    This contains a reference to it.
47104      * @type {Roo.View}
47105      */
47106     view : false,
47107     
47108       /**
47109      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
47110      * <pre><code>
47111
47112 layout.addxtype({
47113        xtype : 'Form',
47114        items: [ .... ]
47115    }
47116 );
47117
47118 </code></pre>
47119      * @param {Object} cfg Xtype definition of item to add.
47120      */
47121     
47122     addxtype : function(cfg) {
47123         // add form..
47124         if (cfg.xtype.match(/^Form$/)) {
47125             var el = this.el.createChild();
47126
47127             this.form = new  Roo.form.Form(cfg);
47128             
47129             
47130             if ( this.form.allItems.length) this.form.render(el.dom);
47131             return this.form;
47132         }
47133         // should only have one of theses..
47134         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
47135             // views..
47136             cfg.el = this.el.appendChild(document.createElement("div"));
47137             // factory?
47138             
47139             var ret = new Roo.factory(cfg);
47140             ret.render && ret.render(false, ''); // render blank..
47141             this.view = ret;
47142             return ret;
47143         }
47144         return false;
47145     }
47146 });
47147
47148 /**
47149  * @class Roo.GridPanel
47150  * @extends Roo.ContentPanel
47151  * @constructor
47152  * Create a new GridPanel.
47153  * @param {Roo.grid.Grid} grid The grid for this panel
47154  * @param {String/Object} config A string to set only the panel's title, or a config object
47155  */
47156 Roo.GridPanel = function(grid, config){
47157     
47158   
47159     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
47160         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
47161         
47162     this.wrapper.dom.appendChild(grid.getGridEl().dom);
47163     
47164     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
47165     
47166     if(this.toolbar){
47167         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
47168     }
47169     // xtype created footer. - not sure if will work as we normally have to render first..
47170     if (this.footer && !this.footer.el && this.footer.xtype) {
47171         
47172         this.footer.container = this.grid.getView().getFooterPanel(true);
47173         this.footer.dataSource = this.grid.dataSource;
47174         this.footer = Roo.factory(this.footer, Roo);
47175         
47176     }
47177     
47178     grid.monitorWindowResize = false; // turn off autosizing
47179     grid.autoHeight = false;
47180     grid.autoWidth = false;
47181     this.grid = grid;
47182     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47183 };
47184
47185 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47186     getId : function(){
47187         return this.grid.id;
47188     },
47189     
47190     /**
47191      * Returns the grid for this panel
47192      * @return {Roo.grid.Grid} 
47193      */
47194     getGrid : function(){
47195         return this.grid;    
47196     },
47197     
47198     setSize : function(width, height){
47199         if(!this.ignoreResize(width, height)){
47200             var grid = this.grid;
47201             var size = this.adjustForComponents(width, height);
47202             grid.getGridEl().setSize(size.width, size.height);
47203             grid.autoSize();
47204         }
47205     },
47206     
47207     beforeSlide : function(){
47208         this.grid.getView().scroller.clip();
47209     },
47210     
47211     afterSlide : function(){
47212         this.grid.getView().scroller.unclip();
47213     },
47214     
47215     destroy : function(){
47216         this.grid.destroy();
47217         delete this.grid;
47218         Roo.GridPanel.superclass.destroy.call(this); 
47219     }
47220 });
47221
47222
47223 /**
47224  * @class Roo.NestedLayoutPanel
47225  * @extends Roo.ContentPanel
47226  * @constructor
47227  * Create a new NestedLayoutPanel.
47228  * 
47229  * 
47230  * @param {Roo.BorderLayout} layout The layout for this panel
47231  * @param {String/Object} config A string to set only the title or a config object
47232  */
47233 Roo.NestedLayoutPanel = function(layout, config)
47234 {
47235     // construct with only one argument..
47236     /* FIXME - implement nicer consturctors
47237     if (layout.layout) {
47238         config = layout;
47239         layout = config.layout;
47240         delete config.layout;
47241     }
47242     if (layout.xtype && !layout.getEl) {
47243         // then layout needs constructing..
47244         layout = Roo.factory(layout, Roo);
47245     }
47246     */
47247     
47248     
47249     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47250     
47251     layout.monitorWindowResize = false; // turn off autosizing
47252     this.layout = layout;
47253     this.layout.getEl().addClass("x-layout-nested-layout");
47254     
47255     
47256     
47257     
47258 };
47259
47260 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47261
47262     setSize : function(width, height){
47263         if(!this.ignoreResize(width, height)){
47264             var size = this.adjustForComponents(width, height);
47265             var el = this.layout.getEl();
47266             el.setSize(size.width, size.height);
47267             var touch = el.dom.offsetWidth;
47268             this.layout.layout();
47269             // ie requires a double layout on the first pass
47270             if(Roo.isIE && !this.initialized){
47271                 this.initialized = true;
47272                 this.layout.layout();
47273             }
47274         }
47275     },
47276     
47277     // activate all subpanels if not currently active..
47278     
47279     setActiveState : function(active){
47280         this.active = active;
47281         if(!active){
47282             this.fireEvent("deactivate", this);
47283             return;
47284         }
47285         
47286         this.fireEvent("activate", this);
47287         // not sure if this should happen before or after..
47288         if (!this.layout) {
47289             return; // should not happen..
47290         }
47291         var reg = false;
47292         for (var r in this.layout.regions) {
47293             reg = this.layout.getRegion(r);
47294             if (reg.getActivePanel()) {
47295                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47296                 reg.setActivePanel(reg.getActivePanel());
47297                 continue;
47298             }
47299             if (!reg.panels.length) {
47300                 continue;
47301             }
47302             reg.showPanel(reg.getPanel(0));
47303         }
47304         
47305         
47306         
47307         
47308     },
47309     
47310     /**
47311      * Returns the nested BorderLayout for this panel
47312      * @return {Roo.BorderLayout} 
47313      */
47314     getLayout : function(){
47315         return this.layout;
47316     },
47317     
47318      /**
47319      * Adds a xtype elements to the layout of the nested panel
47320      * <pre><code>
47321
47322 panel.addxtype({
47323        xtype : 'ContentPanel',
47324        region: 'west',
47325        items: [ .... ]
47326    }
47327 );
47328
47329 panel.addxtype({
47330         xtype : 'NestedLayoutPanel',
47331         region: 'west',
47332         layout: {
47333            center: { },
47334            west: { }   
47335         },
47336         items : [ ... list of content panels or nested layout panels.. ]
47337    }
47338 );
47339 </code></pre>
47340      * @param {Object} cfg Xtype definition of item to add.
47341      */
47342     addxtype : function(cfg) {
47343         return this.layout.addxtype(cfg);
47344     
47345     }
47346 });
47347
47348 Roo.ScrollPanel = function(el, config, content){
47349     config = config || {};
47350     config.fitToFrame = true;
47351     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47352     
47353     this.el.dom.style.overflow = "hidden";
47354     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47355     this.el.removeClass("x-layout-inactive-content");
47356     this.el.on("mousewheel", this.onWheel, this);
47357
47358     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47359     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47360     up.unselectable(); down.unselectable();
47361     up.on("click", this.scrollUp, this);
47362     down.on("click", this.scrollDown, this);
47363     up.addClassOnOver("x-scroller-btn-over");
47364     down.addClassOnOver("x-scroller-btn-over");
47365     up.addClassOnClick("x-scroller-btn-click");
47366     down.addClassOnClick("x-scroller-btn-click");
47367     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47368
47369     this.resizeEl = this.el;
47370     this.el = wrap; this.up = up; this.down = down;
47371 };
47372
47373 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47374     increment : 100,
47375     wheelIncrement : 5,
47376     scrollUp : function(){
47377         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47378     },
47379
47380     scrollDown : function(){
47381         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47382     },
47383
47384     afterScroll : function(){
47385         var el = this.resizeEl;
47386         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47387         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47388         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47389     },
47390
47391     setSize : function(){
47392         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47393         this.afterScroll();
47394     },
47395
47396     onWheel : function(e){
47397         var d = e.getWheelDelta();
47398         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47399         this.afterScroll();
47400         e.stopEvent();
47401     },
47402
47403     setContent : function(content, loadScripts){
47404         this.resizeEl.update(content, loadScripts);
47405     }
47406
47407 });
47408
47409
47410
47411
47412
47413
47414
47415
47416
47417 /**
47418  * @class Roo.TreePanel
47419  * @extends Roo.ContentPanel
47420  * @constructor
47421  * Create a new TreePanel. - defaults to fit/scoll contents.
47422  * @param {String/Object} config A string to set only the panel's title, or a config object
47423  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47424  */
47425 Roo.TreePanel = function(config){
47426     var el = config.el;
47427     var tree = config.tree;
47428     delete config.tree; 
47429     delete config.el; // hopefull!
47430     
47431     // wrapper for IE7 strict & safari scroll issue
47432     
47433     var treeEl = el.createChild();
47434     config.resizeEl = treeEl;
47435     
47436     
47437     
47438     Roo.TreePanel.superclass.constructor.call(this, el, config);
47439  
47440  
47441     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47442     //console.log(tree);
47443     this.on('activate', function()
47444     {
47445         if (this.tree.rendered) {
47446             return;
47447         }
47448         //console.log('render tree');
47449         this.tree.render();
47450     });
47451     
47452     this.on('resize',  function (cp, w, h) {
47453             this.tree.innerCt.setWidth(w);
47454             this.tree.innerCt.setHeight(h);
47455             this.tree.innerCt.setStyle('overflow-y', 'auto');
47456     });
47457
47458         
47459     
47460 };
47461
47462 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47463     fitToFrame : true,
47464     autoScroll : true
47465 });
47466
47467
47468
47469
47470
47471
47472
47473
47474
47475
47476
47477 /*
47478  * Based on:
47479  * Ext JS Library 1.1.1
47480  * Copyright(c) 2006-2007, Ext JS, LLC.
47481  *
47482  * Originally Released Under LGPL - original licence link has changed is not relivant.
47483  *
47484  * Fork - LGPL
47485  * <script type="text/javascript">
47486  */
47487  
47488
47489 /**
47490  * @class Roo.ReaderLayout
47491  * @extends Roo.BorderLayout
47492  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47493  * center region containing two nested regions (a top one for a list view and one for item preview below),
47494  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47495  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47496  * expedites the setup of the overall layout and regions for this common application style.
47497  * Example:
47498  <pre><code>
47499 var reader = new Roo.ReaderLayout();
47500 var CP = Roo.ContentPanel;  // shortcut for adding
47501
47502 reader.beginUpdate();
47503 reader.add("north", new CP("north", "North"));
47504 reader.add("west", new CP("west", {title: "West"}));
47505 reader.add("east", new CP("east", {title: "East"}));
47506
47507 reader.regions.listView.add(new CP("listView", "List"));
47508 reader.regions.preview.add(new CP("preview", "Preview"));
47509 reader.endUpdate();
47510 </code></pre>
47511 * @constructor
47512 * Create a new ReaderLayout
47513 * @param {Object} config Configuration options
47514 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47515 * document.body if omitted)
47516 */
47517 Roo.ReaderLayout = function(config, renderTo){
47518     var c = config || {size:{}};
47519     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47520         north: c.north !== false ? Roo.apply({
47521             split:false,
47522             initialSize: 32,
47523             titlebar: false
47524         }, c.north) : false,
47525         west: c.west !== false ? Roo.apply({
47526             split:true,
47527             initialSize: 200,
47528             minSize: 175,
47529             maxSize: 400,
47530             titlebar: true,
47531             collapsible: true,
47532             animate: true,
47533             margins:{left:5,right:0,bottom:5,top:5},
47534             cmargins:{left:5,right:5,bottom:5,top:5}
47535         }, c.west) : false,
47536         east: c.east !== false ? Roo.apply({
47537             split:true,
47538             initialSize: 200,
47539             minSize: 175,
47540             maxSize: 400,
47541             titlebar: true,
47542             collapsible: true,
47543             animate: true,
47544             margins:{left:0,right:5,bottom:5,top:5},
47545             cmargins:{left:5,right:5,bottom:5,top:5}
47546         }, c.east) : false,
47547         center: Roo.apply({
47548             tabPosition: 'top',
47549             autoScroll:false,
47550             closeOnTab: true,
47551             titlebar:false,
47552             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47553         }, c.center)
47554     });
47555
47556     this.el.addClass('x-reader');
47557
47558     this.beginUpdate();
47559
47560     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47561         south: c.preview !== false ? Roo.apply({
47562             split:true,
47563             initialSize: 200,
47564             minSize: 100,
47565             autoScroll:true,
47566             collapsible:true,
47567             titlebar: true,
47568             cmargins:{top:5,left:0, right:0, bottom:0}
47569         }, c.preview) : false,
47570         center: Roo.apply({
47571             autoScroll:false,
47572             titlebar:false,
47573             minHeight:200
47574         }, c.listView)
47575     });
47576     this.add('center', new Roo.NestedLayoutPanel(inner,
47577             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47578
47579     this.endUpdate();
47580
47581     this.regions.preview = inner.getRegion('south');
47582     this.regions.listView = inner.getRegion('center');
47583 };
47584
47585 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47586  * Based on:
47587  * Ext JS Library 1.1.1
47588  * Copyright(c) 2006-2007, Ext JS, LLC.
47589  *
47590  * Originally Released Under LGPL - original licence link has changed is not relivant.
47591  *
47592  * Fork - LGPL
47593  * <script type="text/javascript">
47594  */
47595  
47596 /**
47597  * @class Roo.grid.Grid
47598  * @extends Roo.util.Observable
47599  * This class represents the primary interface of a component based grid control.
47600  * <br><br>Usage:<pre><code>
47601  var grid = new Roo.grid.Grid("my-container-id", {
47602      ds: myDataStore,
47603      cm: myColModel,
47604      selModel: mySelectionModel,
47605      autoSizeColumns: true,
47606      monitorWindowResize: false,
47607      trackMouseOver: true
47608  });
47609  // set any options
47610  grid.render();
47611  * </code></pre>
47612  * <b>Common Problems:</b><br/>
47613  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47614  * element will correct this<br/>
47615  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47616  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47617  * are unpredictable.<br/>
47618  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47619  * grid to calculate dimensions/offsets.<br/>
47620   * @constructor
47621  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47622  * The container MUST have some type of size defined for the grid to fill. The container will be
47623  * automatically set to position relative if it isn't already.
47624  * @param {Object} config A config object that sets properties on this grid.
47625  */
47626 Roo.grid.Grid = function(container, config){
47627         // initialize the container
47628         this.container = Roo.get(container);
47629         this.container.update("");
47630         this.container.setStyle("overflow", "hidden");
47631     this.container.addClass('x-grid-container');
47632
47633     this.id = this.container.id;
47634
47635     Roo.apply(this, config);
47636     // check and correct shorthanded configs
47637     if(this.ds){
47638         this.dataSource = this.ds;
47639         delete this.ds;
47640     }
47641     if(this.cm){
47642         this.colModel = this.cm;
47643         delete this.cm;
47644     }
47645     if(this.sm){
47646         this.selModel = this.sm;
47647         delete this.sm;
47648     }
47649
47650     if (this.selModel) {
47651         this.selModel = Roo.factory(this.selModel, Roo.grid);
47652         this.sm = this.selModel;
47653         this.sm.xmodule = this.xmodule || false;
47654     }
47655     if (typeof(this.colModel.config) == 'undefined') {
47656         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47657         this.cm = this.colModel;
47658         this.cm.xmodule = this.xmodule || false;
47659     }
47660     if (this.dataSource) {
47661         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47662         this.ds = this.dataSource;
47663         this.ds.xmodule = this.xmodule || false;
47664          
47665     }
47666     
47667     
47668     
47669     if(this.width){
47670         this.container.setWidth(this.width);
47671     }
47672
47673     if(this.height){
47674         this.container.setHeight(this.height);
47675     }
47676     /** @private */
47677         this.addEvents({
47678         // raw events
47679         /**
47680          * @event click
47681          * The raw click event for the entire grid.
47682          * @param {Roo.EventObject} e
47683          */
47684         "click" : true,
47685         /**
47686          * @event dblclick
47687          * The raw dblclick event for the entire grid.
47688          * @param {Roo.EventObject} e
47689          */
47690         "dblclick" : true,
47691         /**
47692          * @event contextmenu
47693          * The raw contextmenu event for the entire grid.
47694          * @param {Roo.EventObject} e
47695          */
47696         "contextmenu" : true,
47697         /**
47698          * @event mousedown
47699          * The raw mousedown event for the entire grid.
47700          * @param {Roo.EventObject} e
47701          */
47702         "mousedown" : true,
47703         /**
47704          * @event mouseup
47705          * The raw mouseup event for the entire grid.
47706          * @param {Roo.EventObject} e
47707          */
47708         "mouseup" : true,
47709         /**
47710          * @event mouseover
47711          * The raw mouseover event for the entire grid.
47712          * @param {Roo.EventObject} e
47713          */
47714         "mouseover" : true,
47715         /**
47716          * @event mouseout
47717          * The raw mouseout event for the entire grid.
47718          * @param {Roo.EventObject} e
47719          */
47720         "mouseout" : true,
47721         /**
47722          * @event keypress
47723          * The raw keypress event for the entire grid.
47724          * @param {Roo.EventObject} e
47725          */
47726         "keypress" : true,
47727         /**
47728          * @event keydown
47729          * The raw keydown event for the entire grid.
47730          * @param {Roo.EventObject} e
47731          */
47732         "keydown" : true,
47733
47734         // custom events
47735
47736         /**
47737          * @event cellclick
47738          * Fires when a cell is clicked
47739          * @param {Grid} this
47740          * @param {Number} rowIndex
47741          * @param {Number} columnIndex
47742          * @param {Roo.EventObject} e
47743          */
47744         "cellclick" : true,
47745         /**
47746          * @event celldblclick
47747          * Fires when a cell is double clicked
47748          * @param {Grid} this
47749          * @param {Number} rowIndex
47750          * @param {Number} columnIndex
47751          * @param {Roo.EventObject} e
47752          */
47753         "celldblclick" : true,
47754         /**
47755          * @event rowclick
47756          * Fires when a row is clicked
47757          * @param {Grid} this
47758          * @param {Number} rowIndex
47759          * @param {Roo.EventObject} e
47760          */
47761         "rowclick" : true,
47762         /**
47763          * @event rowdblclick
47764          * Fires when a row is double clicked
47765          * @param {Grid} this
47766          * @param {Number} rowIndex
47767          * @param {Roo.EventObject} e
47768          */
47769         "rowdblclick" : true,
47770         /**
47771          * @event headerclick
47772          * Fires when a header is clicked
47773          * @param {Grid} this
47774          * @param {Number} columnIndex
47775          * @param {Roo.EventObject} e
47776          */
47777         "headerclick" : true,
47778         /**
47779          * @event headerdblclick
47780          * Fires when a header cell is double clicked
47781          * @param {Grid} this
47782          * @param {Number} columnIndex
47783          * @param {Roo.EventObject} e
47784          */
47785         "headerdblclick" : true,
47786         /**
47787          * @event rowcontextmenu
47788          * Fires when a row is right clicked
47789          * @param {Grid} this
47790          * @param {Number} rowIndex
47791          * @param {Roo.EventObject} e
47792          */
47793         "rowcontextmenu" : true,
47794         /**
47795          * @event cellcontextmenu
47796          * Fires when a cell is right clicked
47797          * @param {Grid} this
47798          * @param {Number} rowIndex
47799          * @param {Number} cellIndex
47800          * @param {Roo.EventObject} e
47801          */
47802          "cellcontextmenu" : true,
47803         /**
47804          * @event headercontextmenu
47805          * Fires when a header is right clicked
47806          * @param {Grid} this
47807          * @param {Number} columnIndex
47808          * @param {Roo.EventObject} e
47809          */
47810         "headercontextmenu" : true,
47811         /**
47812          * @event bodyscroll
47813          * Fires when the body element is scrolled
47814          * @param {Number} scrollLeft
47815          * @param {Number} scrollTop
47816          */
47817         "bodyscroll" : true,
47818         /**
47819          * @event columnresize
47820          * Fires when the user resizes a column
47821          * @param {Number} columnIndex
47822          * @param {Number} newSize
47823          */
47824         "columnresize" : true,
47825         /**
47826          * @event columnmove
47827          * Fires when the user moves a column
47828          * @param {Number} oldIndex
47829          * @param {Number} newIndex
47830          */
47831         "columnmove" : true,
47832         /**
47833          * @event startdrag
47834          * Fires when row(s) start being dragged
47835          * @param {Grid} this
47836          * @param {Roo.GridDD} dd The drag drop object
47837          * @param {event} e The raw browser event
47838          */
47839         "startdrag" : true,
47840         /**
47841          * @event enddrag
47842          * Fires when a drag operation is complete
47843          * @param {Grid} this
47844          * @param {Roo.GridDD} dd The drag drop object
47845          * @param {event} e The raw browser event
47846          */
47847         "enddrag" : true,
47848         /**
47849          * @event dragdrop
47850          * Fires when dragged row(s) are dropped on a valid DD target
47851          * @param {Grid} this
47852          * @param {Roo.GridDD} dd The drag drop object
47853          * @param {String} targetId The target drag drop object
47854          * @param {event} e The raw browser event
47855          */
47856         "dragdrop" : true,
47857         /**
47858          * @event dragover
47859          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47860          * @param {Grid} this
47861          * @param {Roo.GridDD} dd The drag drop object
47862          * @param {String} targetId The target drag drop object
47863          * @param {event} e The raw browser event
47864          */
47865         "dragover" : true,
47866         /**
47867          * @event dragenter
47868          *  Fires when the dragged row(s) first cross another DD target while being dragged
47869          * @param {Grid} this
47870          * @param {Roo.GridDD} dd The drag drop object
47871          * @param {String} targetId The target drag drop object
47872          * @param {event} e The raw browser event
47873          */
47874         "dragenter" : true,
47875         /**
47876          * @event dragout
47877          * Fires when the dragged row(s) leave another DD target while being dragged
47878          * @param {Grid} this
47879          * @param {Roo.GridDD} dd The drag drop object
47880          * @param {String} targetId The target drag drop object
47881          * @param {event} e The raw browser event
47882          */
47883         "dragout" : true,
47884         /**
47885          * @event rowclass
47886          * Fires when a row is rendered, so you can change add a style to it.
47887          * @param {GridView} gridview   The grid view
47888          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47889          */
47890         'rowclass' : true,
47891
47892         /**
47893          * @event render
47894          * Fires when the grid is rendered
47895          * @param {Grid} grid
47896          */
47897         'render' : true
47898     });
47899
47900     Roo.grid.Grid.superclass.constructor.call(this);
47901 };
47902 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47903     
47904     /**
47905      * @cfg {String} ddGroup - drag drop group.
47906      */
47907
47908     /**
47909      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47910      */
47911     minColumnWidth : 25,
47912
47913     /**
47914      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47915      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47916      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47917      */
47918     autoSizeColumns : false,
47919
47920     /**
47921      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47922      */
47923     autoSizeHeaders : true,
47924
47925     /**
47926      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47927      */
47928     monitorWindowResize : true,
47929
47930     /**
47931      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47932      * rows measured to get a columns size. Default is 0 (all rows).
47933      */
47934     maxRowsToMeasure : 0,
47935
47936     /**
47937      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47938      */
47939     trackMouseOver : true,
47940
47941     /**
47942     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47943     */
47944     
47945     /**
47946     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47947     */
47948     enableDragDrop : false,
47949     
47950     /**
47951     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47952     */
47953     enableColumnMove : true,
47954     
47955     /**
47956     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47957     */
47958     enableColumnHide : true,
47959     
47960     /**
47961     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47962     */
47963     enableRowHeightSync : false,
47964     
47965     /**
47966     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47967     */
47968     stripeRows : true,
47969     
47970     /**
47971     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47972     */
47973     autoHeight : false,
47974
47975     /**
47976      * @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.
47977      */
47978     autoExpandColumn : false,
47979
47980     /**
47981     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47982     * Default is 50.
47983     */
47984     autoExpandMin : 50,
47985
47986     /**
47987     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47988     */
47989     autoExpandMax : 1000,
47990
47991     /**
47992     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47993     */
47994     view : null,
47995
47996     /**
47997     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47998     */
47999     loadMask : false,
48000     /**
48001     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
48002     */
48003     dropTarget: false,
48004     
48005    
48006     
48007     // private
48008     rendered : false,
48009
48010     /**
48011     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
48012     * of a fixed width. Default is false.
48013     */
48014     /**
48015     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
48016     */
48017     /**
48018      * Called once after all setup has been completed and the grid is ready to be rendered.
48019      * @return {Roo.grid.Grid} this
48020      */
48021     render : function()
48022     {
48023         var c = this.container;
48024         // try to detect autoHeight/width mode
48025         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
48026             this.autoHeight = true;
48027         }
48028         var view = this.getView();
48029         view.init(this);
48030
48031         c.on("click", this.onClick, this);
48032         c.on("dblclick", this.onDblClick, this);
48033         c.on("contextmenu", this.onContextMenu, this);
48034         c.on("keydown", this.onKeyDown, this);
48035
48036         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
48037
48038         this.getSelectionModel().init(this);
48039
48040         view.render();
48041
48042         if(this.loadMask){
48043             this.loadMask = new Roo.LoadMask(this.container,
48044                     Roo.apply({store:this.dataSource}, this.loadMask));
48045         }
48046         
48047         
48048         if (this.toolbar && this.toolbar.xtype) {
48049             this.toolbar.container = this.getView().getHeaderPanel(true);
48050             this.toolbar = new Roo.Toolbar(this.toolbar);
48051         }
48052         if (this.footer && this.footer.xtype) {
48053             this.footer.dataSource = this.getDataSource();
48054             this.footer.container = this.getView().getFooterPanel(true);
48055             this.footer = Roo.factory(this.footer, Roo);
48056         }
48057         if (this.dropTarget && this.dropTarget.xtype) {
48058             delete this.dropTarget.xtype;
48059             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
48060         }
48061         
48062         
48063         this.rendered = true;
48064         this.fireEvent('render', this);
48065         return this;
48066     },
48067
48068         /**
48069          * Reconfigures the grid to use a different Store and Column Model.
48070          * The View will be bound to the new objects and refreshed.
48071          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
48072          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
48073          */
48074     reconfigure : function(dataSource, colModel){
48075         if(this.loadMask){
48076             this.loadMask.destroy();
48077             this.loadMask = new Roo.LoadMask(this.container,
48078                     Roo.apply({store:dataSource}, this.loadMask));
48079         }
48080         this.view.bind(dataSource, colModel);
48081         this.dataSource = dataSource;
48082         this.colModel = colModel;
48083         this.view.refresh(true);
48084     },
48085
48086     // private
48087     onKeyDown : function(e){
48088         this.fireEvent("keydown", e);
48089     },
48090
48091     /**
48092      * Destroy this grid.
48093      * @param {Boolean} removeEl True to remove the element
48094      */
48095     destroy : function(removeEl, keepListeners){
48096         if(this.loadMask){
48097             this.loadMask.destroy();
48098         }
48099         var c = this.container;
48100         c.removeAllListeners();
48101         this.view.destroy();
48102         this.colModel.purgeListeners();
48103         if(!keepListeners){
48104             this.purgeListeners();
48105         }
48106         c.update("");
48107         if(removeEl === true){
48108             c.remove();
48109         }
48110     },
48111
48112     // private
48113     processEvent : function(name, e){
48114         this.fireEvent(name, e);
48115         var t = e.getTarget();
48116         var v = this.view;
48117         var header = v.findHeaderIndex(t);
48118         if(header !== false){
48119             this.fireEvent("header" + name, this, header, e);
48120         }else{
48121             var row = v.findRowIndex(t);
48122             var cell = v.findCellIndex(t);
48123             if(row !== false){
48124                 this.fireEvent("row" + name, this, row, e);
48125                 if(cell !== false){
48126                     this.fireEvent("cell" + name, this, row, cell, e);
48127                 }
48128             }
48129         }
48130     },
48131
48132     // private
48133     onClick : function(e){
48134         this.processEvent("click", e);
48135     },
48136
48137     // private
48138     onContextMenu : function(e, t){
48139         this.processEvent("contextmenu", e);
48140     },
48141
48142     // private
48143     onDblClick : function(e){
48144         this.processEvent("dblclick", e);
48145     },
48146
48147     // private
48148     walkCells : function(row, col, step, fn, scope){
48149         var cm = this.colModel, clen = cm.getColumnCount();
48150         var ds = this.dataSource, rlen = ds.getCount(), first = true;
48151         if(step < 0){
48152             if(col < 0){
48153                 row--;
48154                 first = false;
48155             }
48156             while(row >= 0){
48157                 if(!first){
48158                     col = clen-1;
48159                 }
48160                 first = false;
48161                 while(col >= 0){
48162                     if(fn.call(scope || this, row, col, cm) === true){
48163                         return [row, col];
48164                     }
48165                     col--;
48166                 }
48167                 row--;
48168             }
48169         } else {
48170             if(col >= clen){
48171                 row++;
48172                 first = false;
48173             }
48174             while(row < rlen){
48175                 if(!first){
48176                     col = 0;
48177                 }
48178                 first = false;
48179                 while(col < clen){
48180                     if(fn.call(scope || this, row, col, cm) === true){
48181                         return [row, col];
48182                     }
48183                     col++;
48184                 }
48185                 row++;
48186             }
48187         }
48188         return null;
48189     },
48190
48191     // private
48192     getSelections : function(){
48193         return this.selModel.getSelections();
48194     },
48195
48196     /**
48197      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48198      * but if manual update is required this method will initiate it.
48199      */
48200     autoSize : function(){
48201         if(this.rendered){
48202             this.view.layout();
48203             if(this.view.adjustForScroll){
48204                 this.view.adjustForScroll();
48205             }
48206         }
48207     },
48208
48209     /**
48210      * Returns the grid's underlying element.
48211      * @return {Element} The element
48212      */
48213     getGridEl : function(){
48214         return this.container;
48215     },
48216
48217     // private for compatibility, overridden by editor grid
48218     stopEditing : function(){},
48219
48220     /**
48221      * Returns the grid's SelectionModel.
48222      * @return {SelectionModel}
48223      */
48224     getSelectionModel : function(){
48225         if(!this.selModel){
48226             this.selModel = new Roo.grid.RowSelectionModel();
48227         }
48228         return this.selModel;
48229     },
48230
48231     /**
48232      * Returns the grid's DataSource.
48233      * @return {DataSource}
48234      */
48235     getDataSource : function(){
48236         return this.dataSource;
48237     },
48238
48239     /**
48240      * Returns the grid's ColumnModel.
48241      * @return {ColumnModel}
48242      */
48243     getColumnModel : function(){
48244         return this.colModel;
48245     },
48246
48247     /**
48248      * Returns the grid's GridView object.
48249      * @return {GridView}
48250      */
48251     getView : function(){
48252         if(!this.view){
48253             this.view = new Roo.grid.GridView(this.viewConfig);
48254         }
48255         return this.view;
48256     },
48257     /**
48258      * Called to get grid's drag proxy text, by default returns this.ddText.
48259      * @return {String}
48260      */
48261     getDragDropText : function(){
48262         var count = this.selModel.getCount();
48263         return String.format(this.ddText, count, count == 1 ? '' : 's');
48264     }
48265 });
48266 /**
48267  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48268  * %0 is replaced with the number of selected rows.
48269  * @type String
48270  */
48271 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48272  * Based on:
48273  * Ext JS Library 1.1.1
48274  * Copyright(c) 2006-2007, Ext JS, LLC.
48275  *
48276  * Originally Released Under LGPL - original licence link has changed is not relivant.
48277  *
48278  * Fork - LGPL
48279  * <script type="text/javascript">
48280  */
48281  
48282 Roo.grid.AbstractGridView = function(){
48283         this.grid = null;
48284         
48285         this.events = {
48286             "beforerowremoved" : true,
48287             "beforerowsinserted" : true,
48288             "beforerefresh" : true,
48289             "rowremoved" : true,
48290             "rowsinserted" : true,
48291             "rowupdated" : true,
48292             "refresh" : true
48293         };
48294     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48295 };
48296
48297 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48298     rowClass : "x-grid-row",
48299     cellClass : "x-grid-cell",
48300     tdClass : "x-grid-td",
48301     hdClass : "x-grid-hd",
48302     splitClass : "x-grid-hd-split",
48303     
48304         init: function(grid){
48305         this.grid = grid;
48306                 var cid = this.grid.getGridEl().id;
48307         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48308         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48309         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48310         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48311         },
48312         
48313         getColumnRenderers : function(){
48314         var renderers = [];
48315         var cm = this.grid.colModel;
48316         var colCount = cm.getColumnCount();
48317         for(var i = 0; i < colCount; i++){
48318             renderers[i] = cm.getRenderer(i);
48319         }
48320         return renderers;
48321     },
48322     
48323     getColumnIds : function(){
48324         var ids = [];
48325         var cm = this.grid.colModel;
48326         var colCount = cm.getColumnCount();
48327         for(var i = 0; i < colCount; i++){
48328             ids[i] = cm.getColumnId(i);
48329         }
48330         return ids;
48331     },
48332     
48333     getDataIndexes : function(){
48334         if(!this.indexMap){
48335             this.indexMap = this.buildIndexMap();
48336         }
48337         return this.indexMap.colToData;
48338     },
48339     
48340     getColumnIndexByDataIndex : function(dataIndex){
48341         if(!this.indexMap){
48342             this.indexMap = this.buildIndexMap();
48343         }
48344         return this.indexMap.dataToCol[dataIndex];
48345     },
48346     
48347     /**
48348      * Set a css style for a column dynamically. 
48349      * @param {Number} colIndex The index of the column
48350      * @param {String} name The css property name
48351      * @param {String} value The css value
48352      */
48353     setCSSStyle : function(colIndex, name, value){
48354         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48355         Roo.util.CSS.updateRule(selector, name, value);
48356     },
48357     
48358     generateRules : function(cm){
48359         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48360         Roo.util.CSS.removeStyleSheet(rulesId);
48361         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48362             var cid = cm.getColumnId(i);
48363             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48364                          this.tdSelector, cid, " {\n}\n",
48365                          this.hdSelector, cid, " {\n}\n",
48366                          this.splitSelector, cid, " {\n}\n");
48367         }
48368         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48369     }
48370 });/*
48371  * Based on:
48372  * Ext JS Library 1.1.1
48373  * Copyright(c) 2006-2007, Ext JS, LLC.
48374  *
48375  * Originally Released Under LGPL - original licence link has changed is not relivant.
48376  *
48377  * Fork - LGPL
48378  * <script type="text/javascript">
48379  */
48380
48381 // private
48382 // This is a support class used internally by the Grid components
48383 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48384     this.grid = grid;
48385     this.view = grid.getView();
48386     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48387     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48388     if(hd2){
48389         this.setHandleElId(Roo.id(hd));
48390         this.setOuterHandleElId(Roo.id(hd2));
48391     }
48392     this.scroll = false;
48393 };
48394 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48395     maxDragWidth: 120,
48396     getDragData : function(e){
48397         var t = Roo.lib.Event.getTarget(e);
48398         var h = this.view.findHeaderCell(t);
48399         if(h){
48400             return {ddel: h.firstChild, header:h};
48401         }
48402         return false;
48403     },
48404
48405     onInitDrag : function(e){
48406         this.view.headersDisabled = true;
48407         var clone = this.dragData.ddel.cloneNode(true);
48408         clone.id = Roo.id();
48409         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48410         this.proxy.update(clone);
48411         return true;
48412     },
48413
48414     afterValidDrop : function(){
48415         var v = this.view;
48416         setTimeout(function(){
48417             v.headersDisabled = false;
48418         }, 50);
48419     },
48420
48421     afterInvalidDrop : function(){
48422         var v = this.view;
48423         setTimeout(function(){
48424             v.headersDisabled = false;
48425         }, 50);
48426     }
48427 });
48428 /*
48429  * Based on:
48430  * Ext JS Library 1.1.1
48431  * Copyright(c) 2006-2007, Ext JS, LLC.
48432  *
48433  * Originally Released Under LGPL - original licence link has changed is not relivant.
48434  *
48435  * Fork - LGPL
48436  * <script type="text/javascript">
48437  */
48438 // private
48439 // This is a support class used internally by the Grid components
48440 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48441     this.grid = grid;
48442     this.view = grid.getView();
48443     // split the proxies so they don't interfere with mouse events
48444     this.proxyTop = Roo.DomHelper.append(document.body, {
48445         cls:"col-move-top", html:"&#160;"
48446     }, true);
48447     this.proxyBottom = Roo.DomHelper.append(document.body, {
48448         cls:"col-move-bottom", html:"&#160;"
48449     }, true);
48450     this.proxyTop.hide = this.proxyBottom.hide = function(){
48451         this.setLeftTop(-100,-100);
48452         this.setStyle("visibility", "hidden");
48453     };
48454     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48455     // temporarily disabled
48456     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48457     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48458 };
48459 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48460     proxyOffsets : [-4, -9],
48461     fly: Roo.Element.fly,
48462
48463     getTargetFromEvent : function(e){
48464         var t = Roo.lib.Event.getTarget(e);
48465         var cindex = this.view.findCellIndex(t);
48466         if(cindex !== false){
48467             return this.view.getHeaderCell(cindex);
48468         }
48469         return null;
48470     },
48471
48472     nextVisible : function(h){
48473         var v = this.view, cm = this.grid.colModel;
48474         h = h.nextSibling;
48475         while(h){
48476             if(!cm.isHidden(v.getCellIndex(h))){
48477                 return h;
48478             }
48479             h = h.nextSibling;
48480         }
48481         return null;
48482     },
48483
48484     prevVisible : function(h){
48485         var v = this.view, cm = this.grid.colModel;
48486         h = h.prevSibling;
48487         while(h){
48488             if(!cm.isHidden(v.getCellIndex(h))){
48489                 return h;
48490             }
48491             h = h.prevSibling;
48492         }
48493         return null;
48494     },
48495
48496     positionIndicator : function(h, n, e){
48497         var x = Roo.lib.Event.getPageX(e);
48498         var r = Roo.lib.Dom.getRegion(n.firstChild);
48499         var px, pt, py = r.top + this.proxyOffsets[1];
48500         if((r.right - x) <= (r.right-r.left)/2){
48501             px = r.right+this.view.borderWidth;
48502             pt = "after";
48503         }else{
48504             px = r.left;
48505             pt = "before";
48506         }
48507         var oldIndex = this.view.getCellIndex(h);
48508         var newIndex = this.view.getCellIndex(n);
48509
48510         if(this.grid.colModel.isFixed(newIndex)){
48511             return false;
48512         }
48513
48514         var locked = this.grid.colModel.isLocked(newIndex);
48515
48516         if(pt == "after"){
48517             newIndex++;
48518         }
48519         if(oldIndex < newIndex){
48520             newIndex--;
48521         }
48522         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48523             return false;
48524         }
48525         px +=  this.proxyOffsets[0];
48526         this.proxyTop.setLeftTop(px, py);
48527         this.proxyTop.show();
48528         if(!this.bottomOffset){
48529             this.bottomOffset = this.view.mainHd.getHeight();
48530         }
48531         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48532         this.proxyBottom.show();
48533         return pt;
48534     },
48535
48536     onNodeEnter : function(n, dd, e, data){
48537         if(data.header != n){
48538             this.positionIndicator(data.header, n, e);
48539         }
48540     },
48541
48542     onNodeOver : function(n, dd, e, data){
48543         var result = false;
48544         if(data.header != n){
48545             result = this.positionIndicator(data.header, n, e);
48546         }
48547         if(!result){
48548             this.proxyTop.hide();
48549             this.proxyBottom.hide();
48550         }
48551         return result ? this.dropAllowed : this.dropNotAllowed;
48552     },
48553
48554     onNodeOut : function(n, dd, e, data){
48555         this.proxyTop.hide();
48556         this.proxyBottom.hide();
48557     },
48558
48559     onNodeDrop : function(n, dd, e, data){
48560         var h = data.header;
48561         if(h != n){
48562             var cm = this.grid.colModel;
48563             var x = Roo.lib.Event.getPageX(e);
48564             var r = Roo.lib.Dom.getRegion(n.firstChild);
48565             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48566             var oldIndex = this.view.getCellIndex(h);
48567             var newIndex = this.view.getCellIndex(n);
48568             var locked = cm.isLocked(newIndex);
48569             if(pt == "after"){
48570                 newIndex++;
48571             }
48572             if(oldIndex < newIndex){
48573                 newIndex--;
48574             }
48575             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48576                 return false;
48577             }
48578             cm.setLocked(oldIndex, locked, true);
48579             cm.moveColumn(oldIndex, newIndex);
48580             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48581             return true;
48582         }
48583         return false;
48584     }
48585 });
48586 /*
48587  * Based on:
48588  * Ext JS Library 1.1.1
48589  * Copyright(c) 2006-2007, Ext JS, LLC.
48590  *
48591  * Originally Released Under LGPL - original licence link has changed is not relivant.
48592  *
48593  * Fork - LGPL
48594  * <script type="text/javascript">
48595  */
48596   
48597 /**
48598  * @class Roo.grid.GridView
48599  * @extends Roo.util.Observable
48600  *
48601  * @constructor
48602  * @param {Object} config
48603  */
48604 Roo.grid.GridView = function(config){
48605     Roo.grid.GridView.superclass.constructor.call(this);
48606     this.el = null;
48607
48608     Roo.apply(this, config);
48609 };
48610
48611 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48612
48613     
48614     rowClass : "x-grid-row",
48615
48616     cellClass : "x-grid-col",
48617
48618     tdClass : "x-grid-td",
48619
48620     hdClass : "x-grid-hd",
48621
48622     splitClass : "x-grid-split",
48623
48624     sortClasses : ["sort-asc", "sort-desc"],
48625
48626     enableMoveAnim : false,
48627
48628     hlColor: "C3DAF9",
48629
48630     dh : Roo.DomHelper,
48631
48632     fly : Roo.Element.fly,
48633
48634     css : Roo.util.CSS,
48635
48636     borderWidth: 1,
48637
48638     splitOffset: 3,
48639
48640     scrollIncrement : 22,
48641
48642     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48643
48644     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48645
48646     bind : function(ds, cm){
48647         if(this.ds){
48648             this.ds.un("load", this.onLoad, this);
48649             this.ds.un("datachanged", this.onDataChange, this);
48650             this.ds.un("add", this.onAdd, this);
48651             this.ds.un("remove", this.onRemove, this);
48652             this.ds.un("update", this.onUpdate, this);
48653             this.ds.un("clear", this.onClear, this);
48654         }
48655         if(ds){
48656             ds.on("load", this.onLoad, this);
48657             ds.on("datachanged", this.onDataChange, this);
48658             ds.on("add", this.onAdd, this);
48659             ds.on("remove", this.onRemove, this);
48660             ds.on("update", this.onUpdate, this);
48661             ds.on("clear", this.onClear, this);
48662         }
48663         this.ds = ds;
48664
48665         if(this.cm){
48666             this.cm.un("widthchange", this.onColWidthChange, this);
48667             this.cm.un("headerchange", this.onHeaderChange, this);
48668             this.cm.un("hiddenchange", this.onHiddenChange, this);
48669             this.cm.un("columnmoved", this.onColumnMove, this);
48670             this.cm.un("columnlockchange", this.onColumnLock, this);
48671         }
48672         if(cm){
48673             this.generateRules(cm);
48674             cm.on("widthchange", this.onColWidthChange, this);
48675             cm.on("headerchange", this.onHeaderChange, this);
48676             cm.on("hiddenchange", this.onHiddenChange, this);
48677             cm.on("columnmoved", this.onColumnMove, this);
48678             cm.on("columnlockchange", this.onColumnLock, this);
48679         }
48680         this.cm = cm;
48681     },
48682
48683     init: function(grid){
48684         Roo.grid.GridView.superclass.init.call(this, grid);
48685
48686         this.bind(grid.dataSource, grid.colModel);
48687
48688         grid.on("headerclick", this.handleHeaderClick, this);
48689
48690         if(grid.trackMouseOver){
48691             grid.on("mouseover", this.onRowOver, this);
48692             grid.on("mouseout", this.onRowOut, this);
48693         }
48694         grid.cancelTextSelection = function(){};
48695         this.gridId = grid.id;
48696
48697         var tpls = this.templates || {};
48698
48699         if(!tpls.master){
48700             tpls.master = new Roo.Template(
48701                '<div class="x-grid" hidefocus="true">',
48702                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48703                   '<div class="x-grid-topbar"></div>',
48704                   '<div class="x-grid-scroller"><div></div></div>',
48705                   '<div class="x-grid-locked">',
48706                       '<div class="x-grid-header">{lockedHeader}</div>',
48707                       '<div class="x-grid-body">{lockedBody}</div>',
48708                   "</div>",
48709                   '<div class="x-grid-viewport">',
48710                       '<div class="x-grid-header">{header}</div>',
48711                       '<div class="x-grid-body">{body}</div>',
48712                   "</div>",
48713                   '<div class="x-grid-bottombar"></div>',
48714                  
48715                   '<div class="x-grid-resize-proxy">&#160;</div>',
48716                "</div>"
48717             );
48718             tpls.master.disableformats = true;
48719         }
48720
48721         if(!tpls.header){
48722             tpls.header = new Roo.Template(
48723                '<table border="0" cellspacing="0" cellpadding="0">',
48724                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48725                "</table>{splits}"
48726             );
48727             tpls.header.disableformats = true;
48728         }
48729         tpls.header.compile();
48730
48731         if(!tpls.hcell){
48732             tpls.hcell = new Roo.Template(
48733                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48734                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48735                 "</div></td>"
48736              );
48737              tpls.hcell.disableFormats = true;
48738         }
48739         tpls.hcell.compile();
48740
48741         if(!tpls.hsplit){
48742             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48743             tpls.hsplit.disableFormats = true;
48744         }
48745         tpls.hsplit.compile();
48746
48747         if(!tpls.body){
48748             tpls.body = new Roo.Template(
48749                '<table border="0" cellspacing="0" cellpadding="0">',
48750                "<tbody>{rows}</tbody>",
48751                "</table>"
48752             );
48753             tpls.body.disableFormats = true;
48754         }
48755         tpls.body.compile();
48756
48757         if(!tpls.row){
48758             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48759             tpls.row.disableFormats = true;
48760         }
48761         tpls.row.compile();
48762
48763         if(!tpls.cell){
48764             tpls.cell = new Roo.Template(
48765                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48766                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48767                 "</td>"
48768             );
48769             tpls.cell.disableFormats = true;
48770         }
48771         tpls.cell.compile();
48772
48773         this.templates = tpls;
48774     },
48775
48776     // remap these for backwards compat
48777     onColWidthChange : function(){
48778         this.updateColumns.apply(this, arguments);
48779     },
48780     onHeaderChange : function(){
48781         this.updateHeaders.apply(this, arguments);
48782     }, 
48783     onHiddenChange : function(){
48784         this.handleHiddenChange.apply(this, arguments);
48785     },
48786     onColumnMove : function(){
48787         this.handleColumnMove.apply(this, arguments);
48788     },
48789     onColumnLock : function(){
48790         this.handleLockChange.apply(this, arguments);
48791     },
48792
48793     onDataChange : function(){
48794         this.refresh();
48795         this.updateHeaderSortState();
48796     },
48797
48798     onClear : function(){
48799         this.refresh();
48800     },
48801
48802     onUpdate : function(ds, record){
48803         this.refreshRow(record);
48804     },
48805
48806     refreshRow : function(record){
48807         var ds = this.ds, index;
48808         if(typeof record == 'number'){
48809             index = record;
48810             record = ds.getAt(index);
48811         }else{
48812             index = ds.indexOf(record);
48813         }
48814         this.insertRows(ds, index, index, true);
48815         this.onRemove(ds, record, index+1, true);
48816         this.syncRowHeights(index, index);
48817         this.layout();
48818         this.fireEvent("rowupdated", this, index, record);
48819     },
48820
48821     onAdd : function(ds, records, index){
48822         this.insertRows(ds, index, index + (records.length-1));
48823     },
48824
48825     onRemove : function(ds, record, index, isUpdate){
48826         if(isUpdate !== true){
48827             this.fireEvent("beforerowremoved", this, index, record);
48828         }
48829         var bt = this.getBodyTable(), lt = this.getLockedTable();
48830         if(bt.rows[index]){
48831             bt.firstChild.removeChild(bt.rows[index]);
48832         }
48833         if(lt.rows[index]){
48834             lt.firstChild.removeChild(lt.rows[index]);
48835         }
48836         if(isUpdate !== true){
48837             this.stripeRows(index);
48838             this.syncRowHeights(index, index);
48839             this.layout();
48840             this.fireEvent("rowremoved", this, index, record);
48841         }
48842     },
48843
48844     onLoad : function(){
48845         this.scrollToTop();
48846     },
48847
48848     /**
48849      * Scrolls the grid to the top
48850      */
48851     scrollToTop : function(){
48852         if(this.scroller){
48853             this.scroller.dom.scrollTop = 0;
48854             this.syncScroll();
48855         }
48856     },
48857
48858     /**
48859      * Gets a panel in the header of the grid that can be used for toolbars etc.
48860      * After modifying the contents of this panel a call to grid.autoSize() may be
48861      * required to register any changes in size.
48862      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48863      * @return Roo.Element
48864      */
48865     getHeaderPanel : function(doShow){
48866         if(doShow){
48867             this.headerPanel.show();
48868         }
48869         return this.headerPanel;
48870     },
48871
48872     /**
48873      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48874      * After modifying the contents of this panel a call to grid.autoSize() may be
48875      * required to register any changes in size.
48876      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48877      * @return Roo.Element
48878      */
48879     getFooterPanel : function(doShow){
48880         if(doShow){
48881             this.footerPanel.show();
48882         }
48883         return this.footerPanel;
48884     },
48885
48886     initElements : function(){
48887         var E = Roo.Element;
48888         var el = this.grid.getGridEl().dom.firstChild;
48889         var cs = el.childNodes;
48890
48891         this.el = new E(el);
48892         
48893          this.focusEl = new E(el.firstChild);
48894         this.focusEl.swallowEvent("click", true);
48895         
48896         this.headerPanel = new E(cs[1]);
48897         this.headerPanel.enableDisplayMode("block");
48898
48899         this.scroller = new E(cs[2]);
48900         this.scrollSizer = new E(this.scroller.dom.firstChild);
48901
48902         this.lockedWrap = new E(cs[3]);
48903         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48904         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48905
48906         this.mainWrap = new E(cs[4]);
48907         this.mainHd = new E(this.mainWrap.dom.firstChild);
48908         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48909
48910         this.footerPanel = new E(cs[5]);
48911         this.footerPanel.enableDisplayMode("block");
48912
48913         this.resizeProxy = new E(cs[6]);
48914
48915         this.headerSelector = String.format(
48916            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48917            this.lockedHd.id, this.mainHd.id
48918         );
48919
48920         this.splitterSelector = String.format(
48921            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48922            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48923         );
48924     },
48925     idToCssName : function(s)
48926     {
48927         return s.replace(/[^a-z0-9]+/ig, '-');
48928     },
48929
48930     getHeaderCell : function(index){
48931         return Roo.DomQuery.select(this.headerSelector)[index];
48932     },
48933
48934     getHeaderCellMeasure : function(index){
48935         return this.getHeaderCell(index).firstChild;
48936     },
48937
48938     getHeaderCellText : function(index){
48939         return this.getHeaderCell(index).firstChild.firstChild;
48940     },
48941
48942     getLockedTable : function(){
48943         return this.lockedBody.dom.firstChild;
48944     },
48945
48946     getBodyTable : function(){
48947         return this.mainBody.dom.firstChild;
48948     },
48949
48950     getLockedRow : function(index){
48951         return this.getLockedTable().rows[index];
48952     },
48953
48954     getRow : function(index){
48955         return this.getBodyTable().rows[index];
48956     },
48957
48958     getRowComposite : function(index){
48959         if(!this.rowEl){
48960             this.rowEl = new Roo.CompositeElementLite();
48961         }
48962         var els = [], lrow, mrow;
48963         if(lrow = this.getLockedRow(index)){
48964             els.push(lrow);
48965         }
48966         if(mrow = this.getRow(index)){
48967             els.push(mrow);
48968         }
48969         this.rowEl.elements = els;
48970         return this.rowEl;
48971     },
48972     /**
48973      * Gets the 'td' of the cell
48974      * 
48975      * @param {Integer} rowIndex row to select
48976      * @param {Integer} colIndex column to select
48977      * 
48978      * @return {Object} 
48979      */
48980     getCell : function(rowIndex, colIndex){
48981         var locked = this.cm.getLockedCount();
48982         var source;
48983         if(colIndex < locked){
48984             source = this.lockedBody.dom.firstChild;
48985         }else{
48986             source = this.mainBody.dom.firstChild;
48987             colIndex -= locked;
48988         }
48989         return source.rows[rowIndex].childNodes[colIndex];
48990     },
48991
48992     getCellText : function(rowIndex, colIndex){
48993         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48994     },
48995
48996     getCellBox : function(cell){
48997         var b = this.fly(cell).getBox();
48998         if(Roo.isOpera){ // opera fails to report the Y
48999             b.y = cell.offsetTop + this.mainBody.getY();
49000         }
49001         return b;
49002     },
49003
49004     getCellIndex : function(cell){
49005         var id = String(cell.className).match(this.cellRE);
49006         if(id){
49007             return parseInt(id[1], 10);
49008         }
49009         return 0;
49010     },
49011
49012     findHeaderIndex : function(n){
49013         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49014         return r ? this.getCellIndex(r) : false;
49015     },
49016
49017     findHeaderCell : function(n){
49018         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49019         return r ? r : false;
49020     },
49021
49022     findRowIndex : function(n){
49023         if(!n){
49024             return false;
49025         }
49026         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
49027         return r ? r.rowIndex : false;
49028     },
49029
49030     findCellIndex : function(node){
49031         var stop = this.el.dom;
49032         while(node && node != stop){
49033             if(this.findRE.test(node.className)){
49034                 return this.getCellIndex(node);
49035             }
49036             node = node.parentNode;
49037         }
49038         return false;
49039     },
49040
49041     getColumnId : function(index){
49042         return this.cm.getColumnId(index);
49043     },
49044
49045     getSplitters : function()
49046     {
49047         if(this.splitterSelector){
49048            return Roo.DomQuery.select(this.splitterSelector);
49049         }else{
49050             return null;
49051       }
49052     },
49053
49054     getSplitter : function(index){
49055         return this.getSplitters()[index];
49056     },
49057
49058     onRowOver : function(e, t){
49059         var row;
49060         if((row = this.findRowIndex(t)) !== false){
49061             this.getRowComposite(row).addClass("x-grid-row-over");
49062         }
49063     },
49064
49065     onRowOut : function(e, t){
49066         var row;
49067         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
49068             this.getRowComposite(row).removeClass("x-grid-row-over");
49069         }
49070     },
49071
49072     renderHeaders : function(){
49073         var cm = this.cm;
49074         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
49075         var cb = [], lb = [], sb = [], lsb = [], p = {};
49076         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49077             p.cellId = "x-grid-hd-0-" + i;
49078             p.splitId = "x-grid-csplit-0-" + i;
49079             p.id = cm.getColumnId(i);
49080             p.title = cm.getColumnTooltip(i) || "";
49081             p.value = cm.getColumnHeader(i) || "";
49082             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
49083             if(!cm.isLocked(i)){
49084                 cb[cb.length] = ct.apply(p);
49085                 sb[sb.length] = st.apply(p);
49086             }else{
49087                 lb[lb.length] = ct.apply(p);
49088                 lsb[lsb.length] = st.apply(p);
49089             }
49090         }
49091         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
49092                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
49093     },
49094
49095     updateHeaders : function(){
49096         var html = this.renderHeaders();
49097         this.lockedHd.update(html[0]);
49098         this.mainHd.update(html[1]);
49099     },
49100
49101     /**
49102      * Focuses the specified row.
49103      * @param {Number} row The row index
49104      */
49105     focusRow : function(row)
49106     {
49107         //Roo.log('GridView.focusRow');
49108         var x = this.scroller.dom.scrollLeft;
49109         this.focusCell(row, 0, false);
49110         this.scroller.dom.scrollLeft = x;
49111     },
49112
49113     /**
49114      * Focuses the specified cell.
49115      * @param {Number} row The row index
49116      * @param {Number} col The column index
49117      * @param {Boolean} hscroll false to disable horizontal scrolling
49118      */
49119     focusCell : function(row, col, hscroll)
49120     {
49121         //Roo.log('GridView.focusCell');
49122         var el = this.ensureVisible(row, col, hscroll);
49123         this.focusEl.alignTo(el, "tl-tl");
49124         if(Roo.isGecko){
49125             this.focusEl.focus();
49126         }else{
49127             this.focusEl.focus.defer(1, this.focusEl);
49128         }
49129     },
49130
49131     /**
49132      * Scrolls the specified cell into view
49133      * @param {Number} row The row index
49134      * @param {Number} col The column index
49135      * @param {Boolean} hscroll false to disable horizontal scrolling
49136      */
49137     ensureVisible : function(row, col, hscroll)
49138     {
49139         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
49140         //return null; //disable for testing.
49141         if(typeof row != "number"){
49142             row = row.rowIndex;
49143         }
49144         if(row < 0 && row >= this.ds.getCount()){
49145             return  null;
49146         }
49147         col = (col !== undefined ? col : 0);
49148         var cm = this.grid.colModel;
49149         while(cm.isHidden(col)){
49150             col++;
49151         }
49152
49153         var el = this.getCell(row, col);
49154         if(!el){
49155             return null;
49156         }
49157         var c = this.scroller.dom;
49158
49159         var ctop = parseInt(el.offsetTop, 10);
49160         var cleft = parseInt(el.offsetLeft, 10);
49161         var cbot = ctop + el.offsetHeight;
49162         var cright = cleft + el.offsetWidth;
49163         
49164         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
49165         var stop = parseInt(c.scrollTop, 10);
49166         var sleft = parseInt(c.scrollLeft, 10);
49167         var sbot = stop + ch;
49168         var sright = sleft + c.clientWidth;
49169         /*
49170         Roo.log('GridView.ensureVisible:' +
49171                 ' ctop:' + ctop +
49172                 ' c.clientHeight:' + c.clientHeight +
49173                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
49174                 ' stop:' + stop +
49175                 ' cbot:' + cbot +
49176                 ' sbot:' + sbot +
49177                 ' ch:' + ch  
49178                 );
49179         */
49180         if(ctop < stop){
49181              c.scrollTop = ctop;
49182             //Roo.log("set scrolltop to ctop DISABLE?");
49183         }else if(cbot > sbot){
49184             //Roo.log("set scrolltop to cbot-ch");
49185             c.scrollTop = cbot-ch;
49186         }
49187         
49188         if(hscroll !== false){
49189             if(cleft < sleft){
49190                 c.scrollLeft = cleft;
49191             }else if(cright > sright){
49192                 c.scrollLeft = cright-c.clientWidth;
49193             }
49194         }
49195          
49196         return el;
49197     },
49198
49199     updateColumns : function(){
49200         this.grid.stopEditing();
49201         var cm = this.grid.colModel, colIds = this.getColumnIds();
49202         //var totalWidth = cm.getTotalWidth();
49203         var pos = 0;
49204         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49205             //if(cm.isHidden(i)) continue;
49206             var w = cm.getColumnWidth(i);
49207             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49208             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49209         }
49210         this.updateSplitters();
49211     },
49212
49213     generateRules : function(cm){
49214         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49215         Roo.util.CSS.removeStyleSheet(rulesId);
49216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49217             var cid = cm.getColumnId(i);
49218             var align = '';
49219             if(cm.config[i].align){
49220                 align = 'text-align:'+cm.config[i].align+';';
49221             }
49222             var hidden = '';
49223             if(cm.isHidden(i)){
49224                 hidden = 'display:none;';
49225             }
49226             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49227             ruleBuf.push(
49228                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49229                     this.hdSelector, cid, " {\n", align, width, "}\n",
49230                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49231                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49232         }
49233         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49234     },
49235
49236     updateSplitters : function(){
49237         var cm = this.cm, s = this.getSplitters();
49238         if(s){ // splitters not created yet
49239             var pos = 0, locked = true;
49240             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49241                 if(cm.isHidden(i)) continue;
49242                 var w = cm.getColumnWidth(i); // make sure it's a number
49243                 if(!cm.isLocked(i) && locked){
49244                     pos = 0;
49245                     locked = false;
49246                 }
49247                 pos += w;
49248                 s[i].style.left = (pos-this.splitOffset) + "px";
49249             }
49250         }
49251     },
49252
49253     handleHiddenChange : function(colModel, colIndex, hidden){
49254         if(hidden){
49255             this.hideColumn(colIndex);
49256         }else{
49257             this.unhideColumn(colIndex);
49258         }
49259     },
49260
49261     hideColumn : function(colIndex){
49262         var cid = this.getColumnId(colIndex);
49263         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49264         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49265         if(Roo.isSafari){
49266             this.updateHeaders();
49267         }
49268         this.updateSplitters();
49269         this.layout();
49270     },
49271
49272     unhideColumn : function(colIndex){
49273         var cid = this.getColumnId(colIndex);
49274         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49275         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49276
49277         if(Roo.isSafari){
49278             this.updateHeaders();
49279         }
49280         this.updateSplitters();
49281         this.layout();
49282     },
49283
49284     insertRows : function(dm, firstRow, lastRow, isUpdate){
49285         if(firstRow == 0 && lastRow == dm.getCount()-1){
49286             this.refresh();
49287         }else{
49288             if(!isUpdate){
49289                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49290             }
49291             var s = this.getScrollState();
49292             var markup = this.renderRows(firstRow, lastRow);
49293             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49294             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49295             this.restoreScroll(s);
49296             if(!isUpdate){
49297                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49298                 this.syncRowHeights(firstRow, lastRow);
49299                 this.stripeRows(firstRow);
49300                 this.layout();
49301             }
49302         }
49303     },
49304
49305     bufferRows : function(markup, target, index){
49306         var before = null, trows = target.rows, tbody = target.tBodies[0];
49307         if(index < trows.length){
49308             before = trows[index];
49309         }
49310         var b = document.createElement("div");
49311         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49312         var rows = b.firstChild.rows;
49313         for(var i = 0, len = rows.length; i < len; i++){
49314             if(before){
49315                 tbody.insertBefore(rows[0], before);
49316             }else{
49317                 tbody.appendChild(rows[0]);
49318             }
49319         }
49320         b.innerHTML = "";
49321         b = null;
49322     },
49323
49324     deleteRows : function(dm, firstRow, lastRow){
49325         if(dm.getRowCount()<1){
49326             this.fireEvent("beforerefresh", this);
49327             this.mainBody.update("");
49328             this.lockedBody.update("");
49329             this.fireEvent("refresh", this);
49330         }else{
49331             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49332             var bt = this.getBodyTable();
49333             var tbody = bt.firstChild;
49334             var rows = bt.rows;
49335             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49336                 tbody.removeChild(rows[firstRow]);
49337             }
49338             this.stripeRows(firstRow);
49339             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49340         }
49341     },
49342
49343     updateRows : function(dataSource, firstRow, lastRow){
49344         var s = this.getScrollState();
49345         this.refresh();
49346         this.restoreScroll(s);
49347     },
49348
49349     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49350         if(!noRefresh){
49351            this.refresh();
49352         }
49353         this.updateHeaderSortState();
49354     },
49355
49356     getScrollState : function(){
49357         
49358         var sb = this.scroller.dom;
49359         return {left: sb.scrollLeft, top: sb.scrollTop};
49360     },
49361
49362     stripeRows : function(startRow){
49363         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49364             return;
49365         }
49366         startRow = startRow || 0;
49367         var rows = this.getBodyTable().rows;
49368         var lrows = this.getLockedTable().rows;
49369         var cls = ' x-grid-row-alt ';
49370         for(var i = startRow, len = rows.length; i < len; i++){
49371             var row = rows[i], lrow = lrows[i];
49372             var isAlt = ((i+1) % 2 == 0);
49373             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49374             if(isAlt == hasAlt){
49375                 continue;
49376             }
49377             if(isAlt){
49378                 row.className += " x-grid-row-alt";
49379             }else{
49380                 row.className = row.className.replace("x-grid-row-alt", "");
49381             }
49382             if(lrow){
49383                 lrow.className = row.className;
49384             }
49385         }
49386     },
49387
49388     restoreScroll : function(state){
49389         //Roo.log('GridView.restoreScroll');
49390         var sb = this.scroller.dom;
49391         sb.scrollLeft = state.left;
49392         sb.scrollTop = state.top;
49393         this.syncScroll();
49394     },
49395
49396     syncScroll : function(){
49397         //Roo.log('GridView.syncScroll');
49398         var sb = this.scroller.dom;
49399         var sh = this.mainHd.dom;
49400         var bs = this.mainBody.dom;
49401         var lv = this.lockedBody.dom;
49402         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49403         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49404     },
49405
49406     handleScroll : function(e){
49407         this.syncScroll();
49408         var sb = this.scroller.dom;
49409         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49410         e.stopEvent();
49411     },
49412
49413     handleWheel : function(e){
49414         var d = e.getWheelDelta();
49415         this.scroller.dom.scrollTop -= d*22;
49416         // set this here to prevent jumpy scrolling on large tables
49417         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49418         e.stopEvent();
49419     },
49420
49421     renderRows : function(startRow, endRow){
49422         // pull in all the crap needed to render rows
49423         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49424         var colCount = cm.getColumnCount();
49425
49426         if(ds.getCount() < 1){
49427             return ["", ""];
49428         }
49429
49430         // build a map for all the columns
49431         var cs = [];
49432         for(var i = 0; i < colCount; i++){
49433             var name = cm.getDataIndex(i);
49434             cs[i] = {
49435                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49436                 renderer : cm.getRenderer(i),
49437                 id : cm.getColumnId(i),
49438                 locked : cm.isLocked(i)
49439             };
49440         }
49441
49442         startRow = startRow || 0;
49443         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49444
49445         // records to render
49446         var rs = ds.getRange(startRow, endRow);
49447
49448         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49449     },
49450
49451     // As much as I hate to duplicate code, this was branched because FireFox really hates
49452     // [].join("") on strings. The performance difference was substantial enough to
49453     // branch this function
49454     doRender : Roo.isGecko ?
49455             function(cs, rs, ds, startRow, colCount, stripe){
49456                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49457                 // buffers
49458                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49459                 
49460                 var hasListener = this.grid.hasListener('rowclass');
49461                 var rowcfg = {};
49462                 for(var j = 0, len = rs.length; j < len; j++){
49463                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49464                     for(var i = 0; i < colCount; i++){
49465                         c = cs[i];
49466                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49467                         p.id = c.id;
49468                         p.css = p.attr = "";
49469                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49470                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49471                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49472                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49473                         }
49474                         var markup = ct.apply(p);
49475                         if(!c.locked){
49476                             cb+= markup;
49477                         }else{
49478                             lcb+= markup;
49479                         }
49480                     }
49481                     var alt = [];
49482                     if(stripe && ((rowIndex+1) % 2 == 0)){
49483                         alt.push("x-grid-row-alt")
49484                     }
49485                     if(r.dirty){
49486                         alt.push(  " x-grid-dirty-row");
49487                     }
49488                     rp.cells = lcb;
49489                     if(this.getRowClass){
49490                         alt.push(this.getRowClass(r, rowIndex));
49491                     }
49492                     if (hasListener) {
49493                         rowcfg = {
49494                              
49495                             record: r,
49496                             rowIndex : rowIndex,
49497                             rowClass : ''
49498                         }
49499                         this.grid.fireEvent('rowclass', this, rowcfg);
49500                         alt.push(rowcfg.rowClass);
49501                     }
49502                     rp.alt = alt.join(" ");
49503                     lbuf+= rt.apply(rp);
49504                     rp.cells = cb;
49505                     buf+=  rt.apply(rp);
49506                 }
49507                 return [lbuf, buf];
49508             } :
49509             function(cs, rs, ds, startRow, colCount, stripe){
49510                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49511                 // buffers
49512                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49513                 var hasListener = this.grid.hasListener('rowclass');
49514  
49515                 var rowcfg = {};
49516                 for(var j = 0, len = rs.length; j < len; j++){
49517                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49518                     for(var i = 0; i < colCount; i++){
49519                         c = cs[i];
49520                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49521                         p.id = c.id;
49522                         p.css = p.attr = "";
49523                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49524                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49525                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49526                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49527                         }
49528                         
49529                         var markup = ct.apply(p);
49530                         if(!c.locked){
49531                             cb[cb.length] = markup;
49532                         }else{
49533                             lcb[lcb.length] = markup;
49534                         }
49535                     }
49536                     var alt = [];
49537                     if(stripe && ((rowIndex+1) % 2 == 0)){
49538                         alt.push( "x-grid-row-alt");
49539                     }
49540                     if(r.dirty){
49541                         alt.push(" x-grid-dirty-row");
49542                     }
49543                     rp.cells = lcb;
49544                     if(this.getRowClass){
49545                         alt.push( this.getRowClass(r, rowIndex));
49546                     }
49547                     if (hasListener) {
49548                         rowcfg = {
49549                              
49550                             record: r,
49551                             rowIndex : rowIndex,
49552                             rowClass : ''
49553                         }
49554                         this.grid.fireEvent('rowclass', this, rowcfg);
49555                         alt.push(rowcfg.rowClass);
49556                     }
49557                     rp.alt = alt.join(" ");
49558                     rp.cells = lcb.join("");
49559                     lbuf[lbuf.length] = rt.apply(rp);
49560                     rp.cells = cb.join("");
49561                     buf[buf.length] =  rt.apply(rp);
49562                 }
49563                 return [lbuf.join(""), buf.join("")];
49564             },
49565
49566     renderBody : function(){
49567         var markup = this.renderRows();
49568         var bt = this.templates.body;
49569         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49570     },
49571
49572     /**
49573      * Refreshes the grid
49574      * @param {Boolean} headersToo
49575      */
49576     refresh : function(headersToo){
49577         this.fireEvent("beforerefresh", this);
49578         this.grid.stopEditing();
49579         var result = this.renderBody();
49580         this.lockedBody.update(result[0]);
49581         this.mainBody.update(result[1]);
49582         if(headersToo === true){
49583             this.updateHeaders();
49584             this.updateColumns();
49585             this.updateSplitters();
49586             this.updateHeaderSortState();
49587         }
49588         this.syncRowHeights();
49589         this.layout();
49590         this.fireEvent("refresh", this);
49591     },
49592
49593     handleColumnMove : function(cm, oldIndex, newIndex){
49594         this.indexMap = null;
49595         var s = this.getScrollState();
49596         this.refresh(true);
49597         this.restoreScroll(s);
49598         this.afterMove(newIndex);
49599     },
49600
49601     afterMove : function(colIndex){
49602         if(this.enableMoveAnim && Roo.enableFx){
49603             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49604         }
49605         // if multisort - fix sortOrder, and reload..
49606         if (this.grid.dataSource.multiSort) {
49607             // the we can call sort again..
49608             var dm = this.grid.dataSource;
49609             var cm = this.grid.colModel;
49610             var so = [];
49611             for(var i = 0; i < cm.config.length; i++ ) {
49612                 
49613                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49614                     continue; // dont' bother, it's not in sort list or being set.
49615                 }
49616                 
49617                 so.push(cm.config[i].dataIndex);
49618             };
49619             dm.sortOrder = so;
49620             dm.load(dm.lastOptions);
49621             
49622             
49623         }
49624         
49625     },
49626
49627     updateCell : function(dm, rowIndex, dataIndex){
49628         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49629         if(typeof colIndex == "undefined"){ // not present in grid
49630             return;
49631         }
49632         var cm = this.grid.colModel;
49633         var cell = this.getCell(rowIndex, colIndex);
49634         var cellText = this.getCellText(rowIndex, colIndex);
49635
49636         var p = {
49637             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49638             id : cm.getColumnId(colIndex),
49639             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49640         };
49641         var renderer = cm.getRenderer(colIndex);
49642         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49643         if(typeof val == "undefined" || val === "") val = "&#160;";
49644         cellText.innerHTML = val;
49645         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49646         this.syncRowHeights(rowIndex, rowIndex);
49647     },
49648
49649     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49650         var maxWidth = 0;
49651         if(this.grid.autoSizeHeaders){
49652             var h = this.getHeaderCellMeasure(colIndex);
49653             maxWidth = Math.max(maxWidth, h.scrollWidth);
49654         }
49655         var tb, index;
49656         if(this.cm.isLocked(colIndex)){
49657             tb = this.getLockedTable();
49658             index = colIndex;
49659         }else{
49660             tb = this.getBodyTable();
49661             index = colIndex - this.cm.getLockedCount();
49662         }
49663         if(tb && tb.rows){
49664             var rows = tb.rows;
49665             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49666             for(var i = 0; i < stopIndex; i++){
49667                 var cell = rows[i].childNodes[index].firstChild;
49668                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49669             }
49670         }
49671         return maxWidth + /*margin for error in IE*/ 5;
49672     },
49673     /**
49674      * Autofit a column to its content.
49675      * @param {Number} colIndex
49676      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49677      */
49678      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49679          if(this.cm.isHidden(colIndex)){
49680              return; // can't calc a hidden column
49681          }
49682         if(forceMinSize){
49683             var cid = this.cm.getColumnId(colIndex);
49684             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49685            if(this.grid.autoSizeHeaders){
49686                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49687            }
49688         }
49689         var newWidth = this.calcColumnWidth(colIndex);
49690         this.cm.setColumnWidth(colIndex,
49691             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49692         if(!suppressEvent){
49693             this.grid.fireEvent("columnresize", colIndex, newWidth);
49694         }
49695     },
49696
49697     /**
49698      * Autofits all columns to their content and then expands to fit any extra space in the grid
49699      */
49700      autoSizeColumns : function(){
49701         var cm = this.grid.colModel;
49702         var colCount = cm.getColumnCount();
49703         for(var i = 0; i < colCount; i++){
49704             this.autoSizeColumn(i, true, true);
49705         }
49706         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49707             this.fitColumns();
49708         }else{
49709             this.updateColumns();
49710             this.layout();
49711         }
49712     },
49713
49714     /**
49715      * Autofits all columns to the grid's width proportionate with their current size
49716      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49717      */
49718     fitColumns : function(reserveScrollSpace){
49719         var cm = this.grid.colModel;
49720         var colCount = cm.getColumnCount();
49721         var cols = [];
49722         var width = 0;
49723         var i, w;
49724         for (i = 0; i < colCount; i++){
49725             if(!cm.isHidden(i) && !cm.isFixed(i)){
49726                 w = cm.getColumnWidth(i);
49727                 cols.push(i);
49728                 cols.push(w);
49729                 width += w;
49730             }
49731         }
49732         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49733         if(reserveScrollSpace){
49734             avail -= 17;
49735         }
49736         var frac = (avail - cm.getTotalWidth())/width;
49737         while (cols.length){
49738             w = cols.pop();
49739             i = cols.pop();
49740             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49741         }
49742         this.updateColumns();
49743         this.layout();
49744     },
49745
49746     onRowSelect : function(rowIndex){
49747         var row = this.getRowComposite(rowIndex);
49748         row.addClass("x-grid-row-selected");
49749     },
49750
49751     onRowDeselect : function(rowIndex){
49752         var row = this.getRowComposite(rowIndex);
49753         row.removeClass("x-grid-row-selected");
49754     },
49755
49756     onCellSelect : function(row, col){
49757         var cell = this.getCell(row, col);
49758         if(cell){
49759             Roo.fly(cell).addClass("x-grid-cell-selected");
49760         }
49761     },
49762
49763     onCellDeselect : function(row, col){
49764         var cell = this.getCell(row, col);
49765         if(cell){
49766             Roo.fly(cell).removeClass("x-grid-cell-selected");
49767         }
49768     },
49769
49770     updateHeaderSortState : function(){
49771         
49772         // sort state can be single { field: xxx, direction : yyy}
49773         // or   { xxx=>ASC , yyy : DESC ..... }
49774         
49775         var mstate = {};
49776         if (!this.ds.multiSort) { 
49777             var state = this.ds.getSortState();
49778             if(!state){
49779                 return;
49780             }
49781             mstate[state.field] = state.direction;
49782             // FIXME... - this is not used here.. but might be elsewhere..
49783             this.sortState = state;
49784             
49785         } else {
49786             mstate = this.ds.sortToggle;
49787         }
49788         //remove existing sort classes..
49789         
49790         var sc = this.sortClasses;
49791         var hds = this.el.select(this.headerSelector).removeClass(sc);
49792         
49793         for(var f in mstate) {
49794         
49795             var sortColumn = this.cm.findColumnIndex(f);
49796             
49797             if(sortColumn != -1){
49798                 var sortDir = mstate[f];        
49799                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49800             }
49801         }
49802         
49803          
49804         
49805     },
49806
49807
49808     handleHeaderClick : function(g, index){
49809         if(this.headersDisabled){
49810             return;
49811         }
49812         var dm = g.dataSource, cm = g.colModel;
49813         if(!cm.isSortable(index)){
49814             return;
49815         }
49816         g.stopEditing();
49817         
49818         if (dm.multiSort) {
49819             // update the sortOrder
49820             var so = [];
49821             for(var i = 0; i < cm.config.length; i++ ) {
49822                 
49823                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49824                     continue; // dont' bother, it's not in sort list or being set.
49825                 }
49826                 
49827                 so.push(cm.config[i].dataIndex);
49828             };
49829             dm.sortOrder = so;
49830         }
49831         
49832         
49833         dm.sort(cm.getDataIndex(index));
49834     },
49835
49836
49837     destroy : function(){
49838         if(this.colMenu){
49839             this.colMenu.removeAll();
49840             Roo.menu.MenuMgr.unregister(this.colMenu);
49841             this.colMenu.getEl().remove();
49842             delete this.colMenu;
49843         }
49844         if(this.hmenu){
49845             this.hmenu.removeAll();
49846             Roo.menu.MenuMgr.unregister(this.hmenu);
49847             this.hmenu.getEl().remove();
49848             delete this.hmenu;
49849         }
49850         if(this.grid.enableColumnMove){
49851             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49852             if(dds){
49853                 for(var dd in dds){
49854                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49855                         var elid = dds[dd].dragElId;
49856                         dds[dd].unreg();
49857                         Roo.get(elid).remove();
49858                     } else if(dds[dd].config.isTarget){
49859                         dds[dd].proxyTop.remove();
49860                         dds[dd].proxyBottom.remove();
49861                         dds[dd].unreg();
49862                     }
49863                     if(Roo.dd.DDM.locationCache[dd]){
49864                         delete Roo.dd.DDM.locationCache[dd];
49865                     }
49866                 }
49867                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49868             }
49869         }
49870         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49871         this.bind(null, null);
49872         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49873     },
49874
49875     handleLockChange : function(){
49876         this.refresh(true);
49877     },
49878
49879     onDenyColumnLock : function(){
49880
49881     },
49882
49883     onDenyColumnHide : function(){
49884
49885     },
49886
49887     handleHdMenuClick : function(item){
49888         var index = this.hdCtxIndex;
49889         var cm = this.cm, ds = this.ds;
49890         switch(item.id){
49891             case "asc":
49892                 ds.sort(cm.getDataIndex(index), "ASC");
49893                 break;
49894             case "desc":
49895                 ds.sort(cm.getDataIndex(index), "DESC");
49896                 break;
49897             case "lock":
49898                 var lc = cm.getLockedCount();
49899                 if(cm.getColumnCount(true) <= lc+1){
49900                     this.onDenyColumnLock();
49901                     return;
49902                 }
49903                 if(lc != index){
49904                     cm.setLocked(index, true, true);
49905                     cm.moveColumn(index, lc);
49906                     this.grid.fireEvent("columnmove", index, lc);
49907                 }else{
49908                     cm.setLocked(index, true);
49909                 }
49910             break;
49911             case "unlock":
49912                 var lc = cm.getLockedCount();
49913                 if((lc-1) != index){
49914                     cm.setLocked(index, false, true);
49915                     cm.moveColumn(index, lc-1);
49916                     this.grid.fireEvent("columnmove", index, lc-1);
49917                 }else{
49918                     cm.setLocked(index, false);
49919                 }
49920             break;
49921             default:
49922                 index = cm.getIndexById(item.id.substr(4));
49923                 if(index != -1){
49924                     if(item.checked && cm.getColumnCount(true) <= 1){
49925                         this.onDenyColumnHide();
49926                         return false;
49927                     }
49928                     cm.setHidden(index, item.checked);
49929                 }
49930         }
49931         return true;
49932     },
49933
49934     beforeColMenuShow : function(){
49935         var cm = this.cm,  colCount = cm.getColumnCount();
49936         this.colMenu.removeAll();
49937         for(var i = 0; i < colCount; i++){
49938             this.colMenu.add(new Roo.menu.CheckItem({
49939                 id: "col-"+cm.getColumnId(i),
49940                 text: cm.getColumnHeader(i),
49941                 checked: !cm.isHidden(i),
49942                 hideOnClick:false
49943             }));
49944         }
49945     },
49946
49947     handleHdCtx : function(g, index, e){
49948         e.stopEvent();
49949         var hd = this.getHeaderCell(index);
49950         this.hdCtxIndex = index;
49951         var ms = this.hmenu.items, cm = this.cm;
49952         ms.get("asc").setDisabled(!cm.isSortable(index));
49953         ms.get("desc").setDisabled(!cm.isSortable(index));
49954         if(this.grid.enableColLock !== false){
49955             ms.get("lock").setDisabled(cm.isLocked(index));
49956             ms.get("unlock").setDisabled(!cm.isLocked(index));
49957         }
49958         this.hmenu.show(hd, "tl-bl");
49959     },
49960
49961     handleHdOver : function(e){
49962         var hd = this.findHeaderCell(e.getTarget());
49963         if(hd && !this.headersDisabled){
49964             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49965                this.fly(hd).addClass("x-grid-hd-over");
49966             }
49967         }
49968     },
49969
49970     handleHdOut : function(e){
49971         var hd = this.findHeaderCell(e.getTarget());
49972         if(hd){
49973             this.fly(hd).removeClass("x-grid-hd-over");
49974         }
49975     },
49976
49977     handleSplitDblClick : function(e, t){
49978         var i = this.getCellIndex(t);
49979         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49980             this.autoSizeColumn(i, true);
49981             this.layout();
49982         }
49983     },
49984
49985     render : function(){
49986
49987         var cm = this.cm;
49988         var colCount = cm.getColumnCount();
49989
49990         if(this.grid.monitorWindowResize === true){
49991             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49992         }
49993         var header = this.renderHeaders();
49994         var body = this.templates.body.apply({rows:""});
49995         var html = this.templates.master.apply({
49996             lockedBody: body,
49997             body: body,
49998             lockedHeader: header[0],
49999             header: header[1]
50000         });
50001
50002         //this.updateColumns();
50003
50004         this.grid.getGridEl().dom.innerHTML = html;
50005
50006         this.initElements();
50007         
50008         // a kludge to fix the random scolling effect in webkit
50009         this.el.on("scroll", function() {
50010             this.el.dom.scrollTop=0; // hopefully not recursive..
50011         },this);
50012
50013         this.scroller.on("scroll", this.handleScroll, this);
50014         this.lockedBody.on("mousewheel", this.handleWheel, this);
50015         this.mainBody.on("mousewheel", this.handleWheel, this);
50016
50017         this.mainHd.on("mouseover", this.handleHdOver, this);
50018         this.mainHd.on("mouseout", this.handleHdOut, this);
50019         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
50020                 {delegate: "."+this.splitClass});
50021
50022         this.lockedHd.on("mouseover", this.handleHdOver, this);
50023         this.lockedHd.on("mouseout", this.handleHdOut, this);
50024         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
50025                 {delegate: "."+this.splitClass});
50026
50027         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
50028             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50029         }
50030
50031         this.updateSplitters();
50032
50033         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
50034             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50035             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50036         }
50037
50038         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
50039             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
50040             this.hmenu.add(
50041                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
50042                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
50043             );
50044             if(this.grid.enableColLock !== false){
50045                 this.hmenu.add('-',
50046                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
50047                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
50048                 );
50049             }
50050             if(this.grid.enableColumnHide !== false){
50051
50052                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
50053                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
50054                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
50055
50056                 this.hmenu.add('-',
50057                     {id:"columns", text: this.columnsText, menu: this.colMenu}
50058                 );
50059             }
50060             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
50061
50062             this.grid.on("headercontextmenu", this.handleHdCtx, this);
50063         }
50064
50065         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
50066             this.dd = new Roo.grid.GridDragZone(this.grid, {
50067                 ddGroup : this.grid.ddGroup || 'GridDD'
50068             });
50069         }
50070
50071         /*
50072         for(var i = 0; i < colCount; i++){
50073             if(cm.isHidden(i)){
50074                 this.hideColumn(i);
50075             }
50076             if(cm.config[i].align){
50077                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
50078                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
50079             }
50080         }*/
50081         
50082         this.updateHeaderSortState();
50083
50084         this.beforeInitialResize();
50085         this.layout(true);
50086
50087         // two part rendering gives faster view to the user
50088         this.renderPhase2.defer(1, this);
50089     },
50090
50091     renderPhase2 : function(){
50092         // render the rows now
50093         this.refresh();
50094         if(this.grid.autoSizeColumns){
50095             this.autoSizeColumns();
50096         }
50097     },
50098
50099     beforeInitialResize : function(){
50100
50101     },
50102
50103     onColumnSplitterMoved : function(i, w){
50104         this.userResized = true;
50105         var cm = this.grid.colModel;
50106         cm.setColumnWidth(i, w, true);
50107         var cid = cm.getColumnId(i);
50108         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50109         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50110         this.updateSplitters();
50111         this.layout();
50112         this.grid.fireEvent("columnresize", i, w);
50113     },
50114
50115     syncRowHeights : function(startIndex, endIndex){
50116         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
50117             startIndex = startIndex || 0;
50118             var mrows = this.getBodyTable().rows;
50119             var lrows = this.getLockedTable().rows;
50120             var len = mrows.length-1;
50121             endIndex = Math.min(endIndex || len, len);
50122             for(var i = startIndex; i <= endIndex; i++){
50123                 var m = mrows[i], l = lrows[i];
50124                 var h = Math.max(m.offsetHeight, l.offsetHeight);
50125                 m.style.height = l.style.height = h + "px";
50126             }
50127         }
50128     },
50129
50130     layout : function(initialRender, is2ndPass){
50131         var g = this.grid;
50132         var auto = g.autoHeight;
50133         var scrollOffset = 16;
50134         var c = g.getGridEl(), cm = this.cm,
50135                 expandCol = g.autoExpandColumn,
50136                 gv = this;
50137         //c.beginMeasure();
50138
50139         if(!c.dom.offsetWidth){ // display:none?
50140             if(initialRender){
50141                 this.lockedWrap.show();
50142                 this.mainWrap.show();
50143             }
50144             return;
50145         }
50146
50147         var hasLock = this.cm.isLocked(0);
50148
50149         var tbh = this.headerPanel.getHeight();
50150         var bbh = this.footerPanel.getHeight();
50151
50152         if(auto){
50153             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
50154             var newHeight = ch + c.getBorderWidth("tb");
50155             if(g.maxHeight){
50156                 newHeight = Math.min(g.maxHeight, newHeight);
50157             }
50158             c.setHeight(newHeight);
50159         }
50160
50161         if(g.autoWidth){
50162             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
50163         }
50164
50165         var s = this.scroller;
50166
50167         var csize = c.getSize(true);
50168
50169         this.el.setSize(csize.width, csize.height);
50170
50171         this.headerPanel.setWidth(csize.width);
50172         this.footerPanel.setWidth(csize.width);
50173
50174         var hdHeight = this.mainHd.getHeight();
50175         var vw = csize.width;
50176         var vh = csize.height - (tbh + bbh);
50177
50178         s.setSize(vw, vh);
50179
50180         var bt = this.getBodyTable();
50181         var ltWidth = hasLock ?
50182                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50183
50184         var scrollHeight = bt.offsetHeight;
50185         var scrollWidth = ltWidth + bt.offsetWidth;
50186         var vscroll = false, hscroll = false;
50187
50188         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50189
50190         var lw = this.lockedWrap, mw = this.mainWrap;
50191         var lb = this.lockedBody, mb = this.mainBody;
50192
50193         setTimeout(function(){
50194             var t = s.dom.offsetTop;
50195             var w = s.dom.clientWidth,
50196                 h = s.dom.clientHeight;
50197
50198             lw.setTop(t);
50199             lw.setSize(ltWidth, h);
50200
50201             mw.setLeftTop(ltWidth, t);
50202             mw.setSize(w-ltWidth, h);
50203
50204             lb.setHeight(h-hdHeight);
50205             mb.setHeight(h-hdHeight);
50206
50207             if(is2ndPass !== true && !gv.userResized && expandCol){
50208                 // high speed resize without full column calculation
50209                 
50210                 var ci = cm.getIndexById(expandCol);
50211                 if (ci < 0) {
50212                     ci = cm.findColumnIndex(expandCol);
50213                 }
50214                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50215                 var expandId = cm.getColumnId(ci);
50216                 var  tw = cm.getTotalWidth(false);
50217                 var currentWidth = cm.getColumnWidth(ci);
50218                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50219                 if(currentWidth != cw){
50220                     cm.setColumnWidth(ci, cw, true);
50221                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50222                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50223                     gv.updateSplitters();
50224                     gv.layout(false, true);
50225                 }
50226             }
50227
50228             if(initialRender){
50229                 lw.show();
50230                 mw.show();
50231             }
50232             //c.endMeasure();
50233         }, 10);
50234     },
50235
50236     onWindowResize : function(){
50237         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50238             return;
50239         }
50240         this.layout();
50241     },
50242
50243     appendFooter : function(parentEl){
50244         return null;
50245     },
50246
50247     sortAscText : "Sort Ascending",
50248     sortDescText : "Sort Descending",
50249     lockText : "Lock Column",
50250     unlockText : "Unlock Column",
50251     columnsText : "Columns"
50252 });
50253
50254
50255 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50256     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50257     this.proxy.el.addClass('x-grid3-col-dd');
50258 };
50259
50260 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50261     handleMouseDown : function(e){
50262
50263     },
50264
50265     callHandleMouseDown : function(e){
50266         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50267     }
50268 });
50269 /*
50270  * Based on:
50271  * Ext JS Library 1.1.1
50272  * Copyright(c) 2006-2007, Ext JS, LLC.
50273  *
50274  * Originally Released Under LGPL - original licence link has changed is not relivant.
50275  *
50276  * Fork - LGPL
50277  * <script type="text/javascript">
50278  */
50279  
50280 // private
50281 // This is a support class used internally by the Grid components
50282 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50283     this.grid = grid;
50284     this.view = grid.getView();
50285     this.proxy = this.view.resizeProxy;
50286     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50287         "gridSplitters" + this.grid.getGridEl().id, {
50288         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50289     });
50290     this.setHandleElId(Roo.id(hd));
50291     this.setOuterHandleElId(Roo.id(hd2));
50292     this.scroll = false;
50293 };
50294 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50295     fly: Roo.Element.fly,
50296
50297     b4StartDrag : function(x, y){
50298         this.view.headersDisabled = true;
50299         this.proxy.setHeight(this.view.mainWrap.getHeight());
50300         var w = this.cm.getColumnWidth(this.cellIndex);
50301         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50302         this.resetConstraints();
50303         this.setXConstraint(minw, 1000);
50304         this.setYConstraint(0, 0);
50305         this.minX = x - minw;
50306         this.maxX = x + 1000;
50307         this.startPos = x;
50308         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50309     },
50310
50311
50312     handleMouseDown : function(e){
50313         ev = Roo.EventObject.setEvent(e);
50314         var t = this.fly(ev.getTarget());
50315         if(t.hasClass("x-grid-split")){
50316             this.cellIndex = this.view.getCellIndex(t.dom);
50317             this.split = t.dom;
50318             this.cm = this.grid.colModel;
50319             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50320                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50321             }
50322         }
50323     },
50324
50325     endDrag : function(e){
50326         this.view.headersDisabled = false;
50327         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50328         var diff = endX - this.startPos;
50329         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50330     },
50331
50332     autoOffset : function(){
50333         this.setDelta(0,0);
50334     }
50335 });/*
50336  * Based on:
50337  * Ext JS Library 1.1.1
50338  * Copyright(c) 2006-2007, Ext JS, LLC.
50339  *
50340  * Originally Released Under LGPL - original licence link has changed is not relivant.
50341  *
50342  * Fork - LGPL
50343  * <script type="text/javascript">
50344  */
50345  
50346 // private
50347 // This is a support class used internally by the Grid components
50348 Roo.grid.GridDragZone = function(grid, config){
50349     this.view = grid.getView();
50350     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50351     if(this.view.lockedBody){
50352         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50353         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50354     }
50355     this.scroll = false;
50356     this.grid = grid;
50357     this.ddel = document.createElement('div');
50358     this.ddel.className = 'x-grid-dd-wrap';
50359 };
50360
50361 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50362     ddGroup : "GridDD",
50363
50364     getDragData : function(e){
50365         var t = Roo.lib.Event.getTarget(e);
50366         var rowIndex = this.view.findRowIndex(t);
50367         if(rowIndex !== false){
50368             var sm = this.grid.selModel;
50369             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50370               //  sm.mouseDown(e, t);
50371             //}
50372             if (e.hasModifier()){
50373                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50374             }
50375             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50376         }
50377         return false;
50378     },
50379
50380     onInitDrag : function(e){
50381         var data = this.dragData;
50382         this.ddel.innerHTML = this.grid.getDragDropText();
50383         this.proxy.update(this.ddel);
50384         // fire start drag?
50385     },
50386
50387     afterRepair : function(){
50388         this.dragging = false;
50389     },
50390
50391     getRepairXY : function(e, data){
50392         return false;
50393     },
50394
50395     onEndDrag : function(data, e){
50396         // fire end drag?
50397     },
50398
50399     onValidDrop : function(dd, e, id){
50400         // fire drag drop?
50401         this.hideProxy();
50402     },
50403
50404     beforeInvalidDrop : function(e, id){
50405
50406     }
50407 });/*
50408  * Based on:
50409  * Ext JS Library 1.1.1
50410  * Copyright(c) 2006-2007, Ext JS, LLC.
50411  *
50412  * Originally Released Under LGPL - original licence link has changed is not relivant.
50413  *
50414  * Fork - LGPL
50415  * <script type="text/javascript">
50416  */
50417  
50418
50419 /**
50420  * @class Roo.grid.ColumnModel
50421  * @extends Roo.util.Observable
50422  * This is the default implementation of a ColumnModel used by the Grid. It defines
50423  * the columns in the grid.
50424  * <br>Usage:<br>
50425  <pre><code>
50426  var colModel = new Roo.grid.ColumnModel([
50427         {header: "Ticker", width: 60, sortable: true, locked: true},
50428         {header: "Company Name", width: 150, sortable: true},
50429         {header: "Market Cap.", width: 100, sortable: true},
50430         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50431         {header: "Employees", width: 100, sortable: true, resizable: false}
50432  ]);
50433  </code></pre>
50434  * <p>
50435  
50436  * The config options listed for this class are options which may appear in each
50437  * individual column definition.
50438  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50439  * @constructor
50440  * @param {Object} config An Array of column config objects. See this class's
50441  * config objects for details.
50442 */
50443 Roo.grid.ColumnModel = function(config){
50444         /**
50445      * The config passed into the constructor
50446      */
50447     this.config = config;
50448     this.lookup = {};
50449
50450     // if no id, create one
50451     // if the column does not have a dataIndex mapping,
50452     // map it to the order it is in the config
50453     for(var i = 0, len = config.length; i < len; i++){
50454         var c = config[i];
50455         if(typeof c.dataIndex == "undefined"){
50456             c.dataIndex = i;
50457         }
50458         if(typeof c.renderer == "string"){
50459             c.renderer = Roo.util.Format[c.renderer];
50460         }
50461         if(typeof c.id == "undefined"){
50462             c.id = Roo.id();
50463         }
50464         if(c.editor && c.editor.xtype){
50465             c.editor  = Roo.factory(c.editor, Roo.grid);
50466         }
50467         if(c.editor && c.editor.isFormField){
50468             c.editor = new Roo.grid.GridEditor(c.editor);
50469         }
50470         this.lookup[c.id] = c;
50471     }
50472
50473     /**
50474      * The width of columns which have no width specified (defaults to 100)
50475      * @type Number
50476      */
50477     this.defaultWidth = 100;
50478
50479     /**
50480      * Default sortable of columns which have no sortable specified (defaults to false)
50481      * @type Boolean
50482      */
50483     this.defaultSortable = false;
50484
50485     this.addEvents({
50486         /**
50487              * @event widthchange
50488              * Fires when the width of a column changes.
50489              * @param {ColumnModel} this
50490              * @param {Number} columnIndex The column index
50491              * @param {Number} newWidth The new width
50492              */
50493             "widthchange": true,
50494         /**
50495              * @event headerchange
50496              * Fires when the text of a header changes.
50497              * @param {ColumnModel} this
50498              * @param {Number} columnIndex The column index
50499              * @param {Number} newText The new header text
50500              */
50501             "headerchange": true,
50502         /**
50503              * @event hiddenchange
50504              * Fires when a column is hidden or "unhidden".
50505              * @param {ColumnModel} this
50506              * @param {Number} columnIndex The column index
50507              * @param {Boolean} hidden true if hidden, false otherwise
50508              */
50509             "hiddenchange": true,
50510             /**
50511          * @event columnmoved
50512          * Fires when a column is moved.
50513          * @param {ColumnModel} this
50514          * @param {Number} oldIndex
50515          * @param {Number} newIndex
50516          */
50517         "columnmoved" : true,
50518         /**
50519          * @event columlockchange
50520          * Fires when a column's locked state is changed
50521          * @param {ColumnModel} this
50522          * @param {Number} colIndex
50523          * @param {Boolean} locked true if locked
50524          */
50525         "columnlockchange" : true
50526     });
50527     Roo.grid.ColumnModel.superclass.constructor.call(this);
50528 };
50529 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50530     /**
50531      * @cfg {String} header The header text to display in the Grid view.
50532      */
50533     /**
50534      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50535      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50536      * specified, the column's index is used as an index into the Record's data Array.
50537      */
50538     /**
50539      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50540      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50541      */
50542     /**
50543      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50544      * Defaults to the value of the {@link #defaultSortable} property.
50545      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50546      */
50547     /**
50548      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50549      */
50550     /**
50551      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50552      */
50553     /**
50554      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50555      */
50556     /**
50557      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50558      */
50559     /**
50560      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50561      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50562      * default renderer uses the raw data value.
50563      */
50564        /**
50565      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50566      */
50567     /**
50568      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50569      */
50570
50571     /**
50572      * Returns the id of the column at the specified index.
50573      * @param {Number} index The column index
50574      * @return {String} the id
50575      */
50576     getColumnId : function(index){
50577         return this.config[index].id;
50578     },
50579
50580     /**
50581      * Returns the column for a specified id.
50582      * @param {String} id The column id
50583      * @return {Object} the column
50584      */
50585     getColumnById : function(id){
50586         return this.lookup[id];
50587     },
50588
50589     
50590     /**
50591      * Returns the column for a specified dataIndex.
50592      * @param {String} dataIndex The column dataIndex
50593      * @return {Object|Boolean} the column or false if not found
50594      */
50595     getColumnByDataIndex: function(dataIndex){
50596         var index = this.findColumnIndex(dataIndex);
50597         return index > -1 ? this.config[index] : false;
50598     },
50599     
50600     /**
50601      * Returns the index for a specified column id.
50602      * @param {String} id The column id
50603      * @return {Number} the index, or -1 if not found
50604      */
50605     getIndexById : function(id){
50606         for(var i = 0, len = this.config.length; i < len; i++){
50607             if(this.config[i].id == id){
50608                 return i;
50609             }
50610         }
50611         return -1;
50612     },
50613     
50614     /**
50615      * Returns the index for a specified column dataIndex.
50616      * @param {String} dataIndex The column dataIndex
50617      * @return {Number} the index, or -1 if not found
50618      */
50619     
50620     findColumnIndex : function(dataIndex){
50621         for(var i = 0, len = this.config.length; i < len; i++){
50622             if(this.config[i].dataIndex == dataIndex){
50623                 return i;
50624             }
50625         }
50626         return -1;
50627     },
50628     
50629     
50630     moveColumn : function(oldIndex, newIndex){
50631         var c = this.config[oldIndex];
50632         this.config.splice(oldIndex, 1);
50633         this.config.splice(newIndex, 0, c);
50634         this.dataMap = null;
50635         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50636     },
50637
50638     isLocked : function(colIndex){
50639         return this.config[colIndex].locked === true;
50640     },
50641
50642     setLocked : function(colIndex, value, suppressEvent){
50643         if(this.isLocked(colIndex) == value){
50644             return;
50645         }
50646         this.config[colIndex].locked = value;
50647         if(!suppressEvent){
50648             this.fireEvent("columnlockchange", this, colIndex, value);
50649         }
50650     },
50651
50652     getTotalLockedWidth : function(){
50653         var totalWidth = 0;
50654         for(var i = 0; i < this.config.length; i++){
50655             if(this.isLocked(i) && !this.isHidden(i)){
50656                 this.totalWidth += this.getColumnWidth(i);
50657             }
50658         }
50659         return totalWidth;
50660     },
50661
50662     getLockedCount : function(){
50663         for(var i = 0, len = this.config.length; i < len; i++){
50664             if(!this.isLocked(i)){
50665                 return i;
50666             }
50667         }
50668     },
50669
50670     /**
50671      * Returns the number of columns.
50672      * @return {Number}
50673      */
50674     getColumnCount : function(visibleOnly){
50675         if(visibleOnly === true){
50676             var c = 0;
50677             for(var i = 0, len = this.config.length; i < len; i++){
50678                 if(!this.isHidden(i)){
50679                     c++;
50680                 }
50681             }
50682             return c;
50683         }
50684         return this.config.length;
50685     },
50686
50687     /**
50688      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50689      * @param {Function} fn
50690      * @param {Object} scope (optional)
50691      * @return {Array} result
50692      */
50693     getColumnsBy : function(fn, scope){
50694         var r = [];
50695         for(var i = 0, len = this.config.length; i < len; i++){
50696             var c = this.config[i];
50697             if(fn.call(scope||this, c, i) === true){
50698                 r[r.length] = c;
50699             }
50700         }
50701         return r;
50702     },
50703
50704     /**
50705      * Returns true if the specified column is sortable.
50706      * @param {Number} col The column index
50707      * @return {Boolean}
50708      */
50709     isSortable : function(col){
50710         if(typeof this.config[col].sortable == "undefined"){
50711             return this.defaultSortable;
50712         }
50713         return this.config[col].sortable;
50714     },
50715
50716     /**
50717      * Returns the rendering (formatting) function defined for the column.
50718      * @param {Number} col The column index.
50719      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50720      */
50721     getRenderer : function(col){
50722         if(!this.config[col].renderer){
50723             return Roo.grid.ColumnModel.defaultRenderer;
50724         }
50725         return this.config[col].renderer;
50726     },
50727
50728     /**
50729      * Sets the rendering (formatting) function for a column.
50730      * @param {Number} col The column index
50731      * @param {Function} fn The function to use to process the cell's raw data
50732      * to return HTML markup for the grid view. The render function is called with
50733      * the following parameters:<ul>
50734      * <li>Data value.</li>
50735      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50736      * <li>css A CSS style string to apply to the table cell.</li>
50737      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50738      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50739      * <li>Row index</li>
50740      * <li>Column index</li>
50741      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50742      */
50743     setRenderer : function(col, fn){
50744         this.config[col].renderer = fn;
50745     },
50746
50747     /**
50748      * Returns the width for the specified column.
50749      * @param {Number} col The column index
50750      * @return {Number}
50751      */
50752     getColumnWidth : function(col){
50753         return this.config[col].width * 1 || this.defaultWidth;
50754     },
50755
50756     /**
50757      * Sets the width for a column.
50758      * @param {Number} col The column index
50759      * @param {Number} width The new width
50760      */
50761     setColumnWidth : function(col, width, suppressEvent){
50762         this.config[col].width = width;
50763         this.totalWidth = null;
50764         if(!suppressEvent){
50765              this.fireEvent("widthchange", this, col, width);
50766         }
50767     },
50768
50769     /**
50770      * Returns the total width of all columns.
50771      * @param {Boolean} includeHidden True to include hidden column widths
50772      * @return {Number}
50773      */
50774     getTotalWidth : function(includeHidden){
50775         if(!this.totalWidth){
50776             this.totalWidth = 0;
50777             for(var i = 0, len = this.config.length; i < len; i++){
50778                 if(includeHidden || !this.isHidden(i)){
50779                     this.totalWidth += this.getColumnWidth(i);
50780                 }
50781             }
50782         }
50783         return this.totalWidth;
50784     },
50785
50786     /**
50787      * Returns the header for the specified column.
50788      * @param {Number} col The column index
50789      * @return {String}
50790      */
50791     getColumnHeader : function(col){
50792         return this.config[col].header;
50793     },
50794
50795     /**
50796      * Sets the header for a column.
50797      * @param {Number} col The column index
50798      * @param {String} header The new header
50799      */
50800     setColumnHeader : function(col, header){
50801         this.config[col].header = header;
50802         this.fireEvent("headerchange", this, col, header);
50803     },
50804
50805     /**
50806      * Returns the tooltip for the specified column.
50807      * @param {Number} col The column index
50808      * @return {String}
50809      */
50810     getColumnTooltip : function(col){
50811             return this.config[col].tooltip;
50812     },
50813     /**
50814      * Sets the tooltip for a column.
50815      * @param {Number} col The column index
50816      * @param {String} tooltip The new tooltip
50817      */
50818     setColumnTooltip : function(col, tooltip){
50819             this.config[col].tooltip = tooltip;
50820     },
50821
50822     /**
50823      * Returns the dataIndex for the specified column.
50824      * @param {Number} col The column index
50825      * @return {Number}
50826      */
50827     getDataIndex : function(col){
50828         return this.config[col].dataIndex;
50829     },
50830
50831     /**
50832      * Sets the dataIndex for a column.
50833      * @param {Number} col The column index
50834      * @param {Number} dataIndex The new dataIndex
50835      */
50836     setDataIndex : function(col, dataIndex){
50837         this.config[col].dataIndex = dataIndex;
50838     },
50839
50840     
50841     
50842     /**
50843      * Returns true if the cell is editable.
50844      * @param {Number} colIndex The column index
50845      * @param {Number} rowIndex The row index
50846      * @return {Boolean}
50847      */
50848     isCellEditable : function(colIndex, rowIndex){
50849         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50850     },
50851
50852     /**
50853      * Returns the editor defined for the cell/column.
50854      * return false or null to disable editing.
50855      * @param {Number} colIndex The column index
50856      * @param {Number} rowIndex The row index
50857      * @return {Object}
50858      */
50859     getCellEditor : function(colIndex, rowIndex){
50860         return this.config[colIndex].editor;
50861     },
50862
50863     /**
50864      * Sets if a column is editable.
50865      * @param {Number} col The column index
50866      * @param {Boolean} editable True if the column is editable
50867      */
50868     setEditable : function(col, editable){
50869         this.config[col].editable = editable;
50870     },
50871
50872
50873     /**
50874      * Returns true if the column is hidden.
50875      * @param {Number} colIndex The column index
50876      * @return {Boolean}
50877      */
50878     isHidden : function(colIndex){
50879         return this.config[colIndex].hidden;
50880     },
50881
50882
50883     /**
50884      * Returns true if the column width cannot be changed
50885      */
50886     isFixed : function(colIndex){
50887         return this.config[colIndex].fixed;
50888     },
50889
50890     /**
50891      * Returns true if the column can be resized
50892      * @return {Boolean}
50893      */
50894     isResizable : function(colIndex){
50895         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50896     },
50897     /**
50898      * Sets if a column is hidden.
50899      * @param {Number} colIndex The column index
50900      * @param {Boolean} hidden True if the column is hidden
50901      */
50902     setHidden : function(colIndex, hidden){
50903         this.config[colIndex].hidden = hidden;
50904         this.totalWidth = null;
50905         this.fireEvent("hiddenchange", this, colIndex, hidden);
50906     },
50907
50908     /**
50909      * Sets the editor for a column.
50910      * @param {Number} col The column index
50911      * @param {Object} editor The editor object
50912      */
50913     setEditor : function(col, editor){
50914         this.config[col].editor = editor;
50915     }
50916 });
50917
50918 Roo.grid.ColumnModel.defaultRenderer = function(value){
50919         if(typeof value == "string" && value.length < 1){
50920             return "&#160;";
50921         }
50922         return value;
50923 };
50924
50925 // Alias for backwards compatibility
50926 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50927 /*
50928  * Based on:
50929  * Ext JS Library 1.1.1
50930  * Copyright(c) 2006-2007, Ext JS, LLC.
50931  *
50932  * Originally Released Under LGPL - original licence link has changed is not relivant.
50933  *
50934  * Fork - LGPL
50935  * <script type="text/javascript">
50936  */
50937
50938 /**
50939  * @class Roo.grid.AbstractSelectionModel
50940  * @extends Roo.util.Observable
50941  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50942  * implemented by descendant classes.  This class should not be directly instantiated.
50943  * @constructor
50944  */
50945 Roo.grid.AbstractSelectionModel = function(){
50946     this.locked = false;
50947     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50948 };
50949
50950 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50951     /** @ignore Called by the grid automatically. Do not call directly. */
50952     init : function(grid){
50953         this.grid = grid;
50954         this.initEvents();
50955     },
50956
50957     /**
50958      * Locks the selections.
50959      */
50960     lock : function(){
50961         this.locked = true;
50962     },
50963
50964     /**
50965      * Unlocks the selections.
50966      */
50967     unlock : function(){
50968         this.locked = false;
50969     },
50970
50971     /**
50972      * Returns true if the selections are locked.
50973      * @return {Boolean}
50974      */
50975     isLocked : function(){
50976         return this.locked;
50977     }
50978 });/*
50979  * Based on:
50980  * Ext JS Library 1.1.1
50981  * Copyright(c) 2006-2007, Ext JS, LLC.
50982  *
50983  * Originally Released Under LGPL - original licence link has changed is not relivant.
50984  *
50985  * Fork - LGPL
50986  * <script type="text/javascript">
50987  */
50988 /**
50989  * @extends Roo.grid.AbstractSelectionModel
50990  * @class Roo.grid.RowSelectionModel
50991  * The default SelectionModel used by {@link Roo.grid.Grid}.
50992  * It supports multiple selections and keyboard selection/navigation. 
50993  * @constructor
50994  * @param {Object} config
50995  */
50996 Roo.grid.RowSelectionModel = function(config){
50997     Roo.apply(this, config);
50998     this.selections = new Roo.util.MixedCollection(false, function(o){
50999         return o.id;
51000     });
51001
51002     this.last = false;
51003     this.lastActive = false;
51004
51005     this.addEvents({
51006         /**
51007              * @event selectionchange
51008              * Fires when the selection changes
51009              * @param {SelectionModel} this
51010              */
51011             "selectionchange" : true,
51012         /**
51013              * @event afterselectionchange
51014              * Fires after the selection changes (eg. by key press or clicking)
51015              * @param {SelectionModel} this
51016              */
51017             "afterselectionchange" : true,
51018         /**
51019              * @event beforerowselect
51020              * Fires when a row is selected being selected, return false to cancel.
51021              * @param {SelectionModel} this
51022              * @param {Number} rowIndex The selected index
51023              * @param {Boolean} keepExisting False if other selections will be cleared
51024              */
51025             "beforerowselect" : true,
51026         /**
51027              * @event rowselect
51028              * Fires when a row is selected.
51029              * @param {SelectionModel} this
51030              * @param {Number} rowIndex The selected index
51031              * @param {Roo.data.Record} r The record
51032              */
51033             "rowselect" : true,
51034         /**
51035              * @event rowdeselect
51036              * Fires when a row is deselected.
51037              * @param {SelectionModel} this
51038              * @param {Number} rowIndex The selected index
51039              */
51040         "rowdeselect" : true
51041     });
51042     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
51043     this.locked = false;
51044 };
51045
51046 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
51047     /**
51048      * @cfg {Boolean} singleSelect
51049      * True to allow selection of only one row at a time (defaults to false)
51050      */
51051     singleSelect : false,
51052
51053     // private
51054     initEvents : function(){
51055
51056         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
51057             this.grid.on("mousedown", this.handleMouseDown, this);
51058         }else{ // allow click to work like normal
51059             this.grid.on("rowclick", this.handleDragableRowClick, this);
51060         }
51061
51062         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
51063             "up" : function(e){
51064                 if(!e.shiftKey){
51065                     this.selectPrevious(e.shiftKey);
51066                 }else if(this.last !== false && this.lastActive !== false){
51067                     var last = this.last;
51068                     this.selectRange(this.last,  this.lastActive-1);
51069                     this.grid.getView().focusRow(this.lastActive);
51070                     if(last !== false){
51071                         this.last = last;
51072                     }
51073                 }else{
51074                     this.selectFirstRow();
51075                 }
51076                 this.fireEvent("afterselectionchange", this);
51077             },
51078             "down" : function(e){
51079                 if(!e.shiftKey){
51080                     this.selectNext(e.shiftKey);
51081                 }else if(this.last !== false && this.lastActive !== false){
51082                     var last = this.last;
51083                     this.selectRange(this.last,  this.lastActive+1);
51084                     this.grid.getView().focusRow(this.lastActive);
51085                     if(last !== false){
51086                         this.last = last;
51087                     }
51088                 }else{
51089                     this.selectFirstRow();
51090                 }
51091                 this.fireEvent("afterselectionchange", this);
51092             },
51093             scope: this
51094         });
51095
51096         var view = this.grid.view;
51097         view.on("refresh", this.onRefresh, this);
51098         view.on("rowupdated", this.onRowUpdated, this);
51099         view.on("rowremoved", this.onRemove, this);
51100     },
51101
51102     // private
51103     onRefresh : function(){
51104         var ds = this.grid.dataSource, i, v = this.grid.view;
51105         var s = this.selections;
51106         s.each(function(r){
51107             if((i = ds.indexOfId(r.id)) != -1){
51108                 v.onRowSelect(i);
51109             }else{
51110                 s.remove(r);
51111             }
51112         });
51113     },
51114
51115     // private
51116     onRemove : function(v, index, r){
51117         this.selections.remove(r);
51118     },
51119
51120     // private
51121     onRowUpdated : function(v, index, r){
51122         if(this.isSelected(r)){
51123             v.onRowSelect(index);
51124         }
51125     },
51126
51127     /**
51128      * Select records.
51129      * @param {Array} records The records to select
51130      * @param {Boolean} keepExisting (optional) True to keep existing selections
51131      */
51132     selectRecords : function(records, keepExisting){
51133         if(!keepExisting){
51134             this.clearSelections();
51135         }
51136         var ds = this.grid.dataSource;
51137         for(var i = 0, len = records.length; i < len; i++){
51138             this.selectRow(ds.indexOf(records[i]), true);
51139         }
51140     },
51141
51142     /**
51143      * Gets the number of selected rows.
51144      * @return {Number}
51145      */
51146     getCount : function(){
51147         return this.selections.length;
51148     },
51149
51150     /**
51151      * Selects the first row in the grid.
51152      */
51153     selectFirstRow : function(){
51154         this.selectRow(0);
51155     },
51156
51157     /**
51158      * Select the last row.
51159      * @param {Boolean} keepExisting (optional) True to keep existing selections
51160      */
51161     selectLastRow : function(keepExisting){
51162         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
51163     },
51164
51165     /**
51166      * Selects the row immediately following the last selected row.
51167      * @param {Boolean} keepExisting (optional) True to keep existing selections
51168      */
51169     selectNext : function(keepExisting){
51170         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
51171             this.selectRow(this.last+1, keepExisting);
51172             this.grid.getView().focusRow(this.last);
51173         }
51174     },
51175
51176     /**
51177      * Selects the row that precedes the last selected row.
51178      * @param {Boolean} keepExisting (optional) True to keep existing selections
51179      */
51180     selectPrevious : function(keepExisting){
51181         if(this.last){
51182             this.selectRow(this.last-1, keepExisting);
51183             this.grid.getView().focusRow(this.last);
51184         }
51185     },
51186
51187     /**
51188      * Returns the selected records
51189      * @return {Array} Array of selected records
51190      */
51191     getSelections : function(){
51192         return [].concat(this.selections.items);
51193     },
51194
51195     /**
51196      * Returns the first selected record.
51197      * @return {Record}
51198      */
51199     getSelected : function(){
51200         return this.selections.itemAt(0);
51201     },
51202
51203
51204     /**
51205      * Clears all selections.
51206      */
51207     clearSelections : function(fast){
51208         if(this.locked) return;
51209         if(fast !== true){
51210             var ds = this.grid.dataSource;
51211             var s = this.selections;
51212             s.each(function(r){
51213                 this.deselectRow(ds.indexOfId(r.id));
51214             }, this);
51215             s.clear();
51216         }else{
51217             this.selections.clear();
51218         }
51219         this.last = false;
51220     },
51221
51222
51223     /**
51224      * Selects all rows.
51225      */
51226     selectAll : function(){
51227         if(this.locked) return;
51228         this.selections.clear();
51229         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51230             this.selectRow(i, true);
51231         }
51232     },
51233
51234     /**
51235      * Returns True if there is a selection.
51236      * @return {Boolean}
51237      */
51238     hasSelection : function(){
51239         return this.selections.length > 0;
51240     },
51241
51242     /**
51243      * Returns True if the specified row is selected.
51244      * @param {Number/Record} record The record or index of the record to check
51245      * @return {Boolean}
51246      */
51247     isSelected : function(index){
51248         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51249         return (r && this.selections.key(r.id) ? true : false);
51250     },
51251
51252     /**
51253      * Returns True if the specified record id is selected.
51254      * @param {String} id The id of record to check
51255      * @return {Boolean}
51256      */
51257     isIdSelected : function(id){
51258         return (this.selections.key(id) ? true : false);
51259     },
51260
51261     // private
51262     handleMouseDown : function(e, t){
51263         var view = this.grid.getView(), rowIndex;
51264         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51265             return;
51266         };
51267         if(e.shiftKey && this.last !== false){
51268             var last = this.last;
51269             this.selectRange(last, rowIndex, e.ctrlKey);
51270             this.last = last; // reset the last
51271             view.focusRow(rowIndex);
51272         }else{
51273             var isSelected = this.isSelected(rowIndex);
51274             if(e.button !== 0 && isSelected){
51275                 view.focusRow(rowIndex);
51276             }else if(e.ctrlKey && isSelected){
51277                 this.deselectRow(rowIndex);
51278             }else if(!isSelected){
51279                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51280                 view.focusRow(rowIndex);
51281             }
51282         }
51283         this.fireEvent("afterselectionchange", this);
51284     },
51285     // private
51286     handleDragableRowClick :  function(grid, rowIndex, e) 
51287     {
51288         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51289             this.selectRow(rowIndex, false);
51290             grid.view.focusRow(rowIndex);
51291              this.fireEvent("afterselectionchange", this);
51292         }
51293     },
51294     
51295     /**
51296      * Selects multiple rows.
51297      * @param {Array} rows Array of the indexes of the row to select
51298      * @param {Boolean} keepExisting (optional) True to keep existing selections
51299      */
51300     selectRows : function(rows, keepExisting){
51301         if(!keepExisting){
51302             this.clearSelections();
51303         }
51304         for(var i = 0, len = rows.length; i < len; i++){
51305             this.selectRow(rows[i], true);
51306         }
51307     },
51308
51309     /**
51310      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51311      * @param {Number} startRow The index of the first row in the range
51312      * @param {Number} endRow The index of the last row in the range
51313      * @param {Boolean} keepExisting (optional) True to retain existing selections
51314      */
51315     selectRange : function(startRow, endRow, keepExisting){
51316         if(this.locked) return;
51317         if(!keepExisting){
51318             this.clearSelections();
51319         }
51320         if(startRow <= endRow){
51321             for(var i = startRow; i <= endRow; i++){
51322                 this.selectRow(i, true);
51323             }
51324         }else{
51325             for(var i = startRow; i >= endRow; i--){
51326                 this.selectRow(i, true);
51327             }
51328         }
51329     },
51330
51331     /**
51332      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51333      * @param {Number} startRow The index of the first row in the range
51334      * @param {Number} endRow The index of the last row in the range
51335      */
51336     deselectRange : function(startRow, endRow, preventViewNotify){
51337         if(this.locked) return;
51338         for(var i = startRow; i <= endRow; i++){
51339             this.deselectRow(i, preventViewNotify);
51340         }
51341     },
51342
51343     /**
51344      * Selects a row.
51345      * @param {Number} row The index of the row to select
51346      * @param {Boolean} keepExisting (optional) True to keep existing selections
51347      */
51348     selectRow : function(index, keepExisting, preventViewNotify){
51349         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51350         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51351             if(!keepExisting || this.singleSelect){
51352                 this.clearSelections();
51353             }
51354             var r = this.grid.dataSource.getAt(index);
51355             this.selections.add(r);
51356             this.last = this.lastActive = index;
51357             if(!preventViewNotify){
51358                 this.grid.getView().onRowSelect(index);
51359             }
51360             this.fireEvent("rowselect", this, index, r);
51361             this.fireEvent("selectionchange", this);
51362         }
51363     },
51364
51365     /**
51366      * Deselects a row.
51367      * @param {Number} row The index of the row to deselect
51368      */
51369     deselectRow : function(index, preventViewNotify){
51370         if(this.locked) return;
51371         if(this.last == index){
51372             this.last = false;
51373         }
51374         if(this.lastActive == index){
51375             this.lastActive = false;
51376         }
51377         var r = this.grid.dataSource.getAt(index);
51378         this.selections.remove(r);
51379         if(!preventViewNotify){
51380             this.grid.getView().onRowDeselect(index);
51381         }
51382         this.fireEvent("rowdeselect", this, index);
51383         this.fireEvent("selectionchange", this);
51384     },
51385
51386     // private
51387     restoreLast : function(){
51388         if(this._last){
51389             this.last = this._last;
51390         }
51391     },
51392
51393     // private
51394     acceptsNav : function(row, col, cm){
51395         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51396     },
51397
51398     // private
51399     onEditorKey : function(field, e){
51400         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51401         if(k == e.TAB){
51402             e.stopEvent();
51403             ed.completeEdit();
51404             if(e.shiftKey){
51405                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51406             }else{
51407                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51408             }
51409         }else if(k == e.ENTER && !e.ctrlKey){
51410             e.stopEvent();
51411             ed.completeEdit();
51412             if(e.shiftKey){
51413                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51414             }else{
51415                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51416             }
51417         }else if(k == e.ESC){
51418             ed.cancelEdit();
51419         }
51420         if(newCell){
51421             g.startEditing(newCell[0], newCell[1]);
51422         }
51423     }
51424 });/*
51425  * Based on:
51426  * Ext JS Library 1.1.1
51427  * Copyright(c) 2006-2007, Ext JS, LLC.
51428  *
51429  * Originally Released Under LGPL - original licence link has changed is not relivant.
51430  *
51431  * Fork - LGPL
51432  * <script type="text/javascript">
51433  */
51434 /**
51435  * @class Roo.grid.CellSelectionModel
51436  * @extends Roo.grid.AbstractSelectionModel
51437  * This class provides the basic implementation for cell selection in a grid.
51438  * @constructor
51439  * @param {Object} config The object containing the configuration of this model.
51440  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51441  */
51442 Roo.grid.CellSelectionModel = function(config){
51443     Roo.apply(this, config);
51444
51445     this.selection = null;
51446
51447     this.addEvents({
51448         /**
51449              * @event beforerowselect
51450              * Fires before a cell is selected.
51451              * @param {SelectionModel} this
51452              * @param {Number} rowIndex The selected row index
51453              * @param {Number} colIndex The selected cell index
51454              */
51455             "beforecellselect" : true,
51456         /**
51457              * @event cellselect
51458              * Fires when a cell is selected.
51459              * @param {SelectionModel} this
51460              * @param {Number} rowIndex The selected row index
51461              * @param {Number} colIndex The selected cell index
51462              */
51463             "cellselect" : true,
51464         /**
51465              * @event selectionchange
51466              * Fires when the active selection changes.
51467              * @param {SelectionModel} this
51468              * @param {Object} selection null for no selection or an object (o) with two properties
51469                 <ul>
51470                 <li>o.record: the record object for the row the selection is in</li>
51471                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51472                 </ul>
51473              */
51474             "selectionchange" : true,
51475         /**
51476              * @event tabend
51477              * Fires when the tab (or enter) was pressed on the last editable cell
51478              * You can use this to trigger add new row.
51479              * @param {SelectionModel} this
51480              */
51481             "tabend" : true,
51482          /**
51483              * @event beforeeditnext
51484              * Fires before the next editable sell is made active
51485              * You can use this to skip to another cell or fire the tabend
51486              *    if you set cell to false
51487              * @param {Object} eventdata object : { cell : [ row, col ] } 
51488              */
51489             "beforeeditnext" : true
51490     });
51491     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51492 };
51493
51494 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51495     
51496     enter_is_tab: false,
51497
51498     /** @ignore */
51499     initEvents : function(){
51500         this.grid.on("mousedown", this.handleMouseDown, this);
51501         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51502         var view = this.grid.view;
51503         view.on("refresh", this.onViewChange, this);
51504         view.on("rowupdated", this.onRowUpdated, this);
51505         view.on("beforerowremoved", this.clearSelections, this);
51506         view.on("beforerowsinserted", this.clearSelections, this);
51507         if(this.grid.isEditor){
51508             this.grid.on("beforeedit", this.beforeEdit,  this);
51509         }
51510     },
51511
51512         //private
51513     beforeEdit : function(e){
51514         this.select(e.row, e.column, false, true, e.record);
51515     },
51516
51517         //private
51518     onRowUpdated : function(v, index, r){
51519         if(this.selection && this.selection.record == r){
51520             v.onCellSelect(index, this.selection.cell[1]);
51521         }
51522     },
51523
51524         //private
51525     onViewChange : function(){
51526         this.clearSelections(true);
51527     },
51528
51529         /**
51530          * Returns the currently selected cell,.
51531          * @return {Array} The selected cell (row, column) or null if none selected.
51532          */
51533     getSelectedCell : function(){
51534         return this.selection ? this.selection.cell : null;
51535     },
51536
51537     /**
51538      * Clears all selections.
51539      * @param {Boolean} true to prevent the gridview from being notified about the change.
51540      */
51541     clearSelections : function(preventNotify){
51542         var s = this.selection;
51543         if(s){
51544             if(preventNotify !== true){
51545                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51546             }
51547             this.selection = null;
51548             this.fireEvent("selectionchange", this, null);
51549         }
51550     },
51551
51552     /**
51553      * Returns true if there is a selection.
51554      * @return {Boolean}
51555      */
51556     hasSelection : function(){
51557         return this.selection ? true : false;
51558     },
51559
51560     /** @ignore */
51561     handleMouseDown : function(e, t){
51562         var v = this.grid.getView();
51563         if(this.isLocked()){
51564             return;
51565         };
51566         var row = v.findRowIndex(t);
51567         var cell = v.findCellIndex(t);
51568         if(row !== false && cell !== false){
51569             this.select(row, cell);
51570         }
51571     },
51572
51573     /**
51574      * Selects a cell.
51575      * @param {Number} rowIndex
51576      * @param {Number} collIndex
51577      */
51578     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51579         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51580             this.clearSelections();
51581             r = r || this.grid.dataSource.getAt(rowIndex);
51582             this.selection = {
51583                 record : r,
51584                 cell : [rowIndex, colIndex]
51585             };
51586             if(!preventViewNotify){
51587                 var v = this.grid.getView();
51588                 v.onCellSelect(rowIndex, colIndex);
51589                 if(preventFocus !== true){
51590                     v.focusCell(rowIndex, colIndex);
51591                 }
51592             }
51593             this.fireEvent("cellselect", this, rowIndex, colIndex);
51594             this.fireEvent("selectionchange", this, this.selection);
51595         }
51596     },
51597
51598         //private
51599     isSelectable : function(rowIndex, colIndex, cm){
51600         return !cm.isHidden(colIndex);
51601     },
51602
51603     /** @ignore */
51604     handleKeyDown : function(e){
51605         //Roo.log('Cell Sel Model handleKeyDown');
51606         if(!e.isNavKeyPress()){
51607             return;
51608         }
51609         var g = this.grid, s = this.selection;
51610         if(!s){
51611             e.stopEvent();
51612             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51613             if(cell){
51614                 this.select(cell[0], cell[1]);
51615             }
51616             return;
51617         }
51618         var sm = this;
51619         var walk = function(row, col, step){
51620             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51621         };
51622         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51623         var newCell;
51624
51625       
51626
51627         switch(k){
51628             case e.TAB:
51629                 // handled by onEditorKey
51630                 if (g.isEditor && g.editing) {
51631                     return;
51632                 }
51633                 if(e.shiftKey) {
51634                     newCell = walk(r, c-1, -1);
51635                 } else {
51636                     newCell = walk(r, c+1, 1);
51637                 }
51638                 break;
51639             
51640             case e.DOWN:
51641                newCell = walk(r+1, c, 1);
51642                 break;
51643             
51644             case e.UP:
51645                 newCell = walk(r-1, c, -1);
51646                 break;
51647             
51648             case e.RIGHT:
51649                 newCell = walk(r, c+1, 1);
51650                 break;
51651             
51652             case e.LEFT:
51653                 newCell = walk(r, c-1, -1);
51654                 break;
51655             
51656             case e.ENTER:
51657                 
51658                 if(g.isEditor && !g.editing){
51659                    g.startEditing(r, c);
51660                    e.stopEvent();
51661                    return;
51662                 }
51663                 
51664                 
51665              break;
51666         };
51667         if(newCell){
51668             this.select(newCell[0], newCell[1]);
51669             e.stopEvent();
51670             
51671         }
51672     },
51673
51674     acceptsNav : function(row, col, cm){
51675         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51676     },
51677     /**
51678      * Selects a cell.
51679      * @param {Number} field (not used) - as it's normally used as a listener
51680      * @param {Number} e - event - fake it by using
51681      *
51682      * var e = Roo.EventObjectImpl.prototype;
51683      * e.keyCode = e.TAB
51684      *
51685      * 
51686      */
51687     onEditorKey : function(field, e){
51688         
51689         var k = e.getKey(),
51690             newCell,
51691             g = this.grid,
51692             ed = g.activeEditor,
51693             forward = false;
51694         ///Roo.log('onEditorKey' + k);
51695         
51696         
51697         if (this.enter_is_tab && k == e.ENTER) {
51698             k = e.TAB;
51699         }
51700         
51701         if(k == e.TAB){
51702             if(e.shiftKey){
51703                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51704             }else{
51705                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51706                 forward = true;
51707             }
51708             
51709             e.stopEvent();
51710             
51711         } else if(k == e.ENTER &&  !e.ctrlKey){
51712             ed.completeEdit();
51713             e.stopEvent();
51714             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51715         
51716                 } else if(k == e.ESC){
51717             ed.cancelEdit();
51718         }
51719                 
51720         if (newCell) {
51721             var ecall = { cell : newCell, forward : forward };
51722             this.fireEvent('beforeeditnext', ecall );
51723             newCell = ecall.cell;
51724                         forward = ecall.forward;
51725         }
51726                 
51727         if(newCell){
51728             //Roo.log('next cell after edit');
51729             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51730         } else if (forward) {
51731             // tabbed past last
51732             this.fireEvent.defer(100, this, ['tabend',this]);
51733         }
51734     }
51735 });/*
51736  * Based on:
51737  * Ext JS Library 1.1.1
51738  * Copyright(c) 2006-2007, Ext JS, LLC.
51739  *
51740  * Originally Released Under LGPL - original licence link has changed is not relivant.
51741  *
51742  * Fork - LGPL
51743  * <script type="text/javascript">
51744  */
51745  
51746 /**
51747  * @class Roo.grid.EditorGrid
51748  * @extends Roo.grid.Grid
51749  * Class for creating and editable grid.
51750  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51751  * The container MUST have some type of size defined for the grid to fill. The container will be 
51752  * automatically set to position relative if it isn't already.
51753  * @param {Object} dataSource The data model to bind to
51754  * @param {Object} colModel The column model with info about this grid's columns
51755  */
51756 Roo.grid.EditorGrid = function(container, config){
51757     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51758     this.getGridEl().addClass("xedit-grid");
51759
51760     if(!this.selModel){
51761         this.selModel = new Roo.grid.CellSelectionModel();
51762     }
51763
51764     this.activeEditor = null;
51765
51766         this.addEvents({
51767             /**
51768              * @event beforeedit
51769              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51770              * <ul style="padding:5px;padding-left:16px;">
51771              * <li>grid - This grid</li>
51772              * <li>record - The record being edited</li>
51773              * <li>field - The field name being edited</li>
51774              * <li>value - The value for the field being edited.</li>
51775              * <li>row - The grid row index</li>
51776              * <li>column - The grid column index</li>
51777              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51778              * </ul>
51779              * @param {Object} e An edit event (see above for description)
51780              */
51781             "beforeedit" : true,
51782             /**
51783              * @event afteredit
51784              * Fires after a cell is edited. <br />
51785              * <ul style="padding:5px;padding-left:16px;">
51786              * <li>grid - This grid</li>
51787              * <li>record - The record being edited</li>
51788              * <li>field - The field name being edited</li>
51789              * <li>value - The value being set</li>
51790              * <li>originalValue - The original value for the field, before the edit.</li>
51791              * <li>row - The grid row index</li>
51792              * <li>column - The grid column index</li>
51793              * </ul>
51794              * @param {Object} e An edit event (see above for description)
51795              */
51796             "afteredit" : true,
51797             /**
51798              * @event validateedit
51799              * Fires after a cell is edited, but before the value is set in the record. 
51800          * You can use this to modify the value being set in the field, Return false
51801              * to cancel the change. The edit event object has the following properties <br />
51802              * <ul style="padding:5px;padding-left:16px;">
51803          * <li>editor - This editor</li>
51804              * <li>grid - This grid</li>
51805              * <li>record - The record being edited</li>
51806              * <li>field - The field name being edited</li>
51807              * <li>value - The value being set</li>
51808              * <li>originalValue - The original value for the field, before the edit.</li>
51809              * <li>row - The grid row index</li>
51810              * <li>column - The grid column index</li>
51811              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51812              * </ul>
51813              * @param {Object} e An edit event (see above for description)
51814              */
51815             "validateedit" : true
51816         });
51817     this.on("bodyscroll", this.stopEditing,  this);
51818     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51819 };
51820
51821 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51822     /**
51823      * @cfg {Number} clicksToEdit
51824      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51825      */
51826     clicksToEdit: 2,
51827
51828     // private
51829     isEditor : true,
51830     // private
51831     trackMouseOver: false, // causes very odd FF errors
51832
51833     onCellDblClick : function(g, row, col){
51834         this.startEditing(row, col);
51835     },
51836
51837     onEditComplete : function(ed, value, startValue){
51838         this.editing = false;
51839         this.activeEditor = null;
51840         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51841         var r = ed.record;
51842         var field = this.colModel.getDataIndex(ed.col);
51843         var e = {
51844             grid: this,
51845             record: r,
51846             field: field,
51847             originalValue: startValue,
51848             value: value,
51849             row: ed.row,
51850             column: ed.col,
51851             cancel:false,
51852             editor: ed
51853         };
51854         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51855         cell.show();
51856           
51857         if(String(value) !== String(startValue)){
51858             
51859             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51860                 r.set(field, e.value);
51861                 // if we are dealing with a combo box..
51862                 // then we also set the 'name' colum to be the displayField
51863                 if (ed.field.displayField && ed.field.name) {
51864                     r.set(ed.field.name, ed.field.el.dom.value);
51865                 }
51866                 
51867                 delete e.cancel; //?? why!!!
51868                 this.fireEvent("afteredit", e);
51869             }
51870         } else {
51871             this.fireEvent("afteredit", e); // always fire it!
51872         }
51873         this.view.focusCell(ed.row, ed.col);
51874     },
51875
51876     /**
51877      * Starts editing the specified for the specified row/column
51878      * @param {Number} rowIndex
51879      * @param {Number} colIndex
51880      */
51881     startEditing : function(row, col){
51882         this.stopEditing();
51883         if(this.colModel.isCellEditable(col, row)){
51884             this.view.ensureVisible(row, col, true);
51885           
51886             var r = this.dataSource.getAt(row);
51887             var field = this.colModel.getDataIndex(col);
51888             var cell = Roo.get(this.view.getCell(row,col));
51889             var e = {
51890                 grid: this,
51891                 record: r,
51892                 field: field,
51893                 value: r.data[field],
51894                 row: row,
51895                 column: col,
51896                 cancel:false 
51897             };
51898             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51899                 this.editing = true;
51900                 var ed = this.colModel.getCellEditor(col, row);
51901                 
51902                 if (!ed) {
51903                     return;
51904                 }
51905                 if(!ed.rendered){
51906                     ed.render(ed.parentEl || document.body);
51907                 }
51908                 ed.field.reset();
51909                
51910                 cell.hide();
51911                 
51912                 (function(){ // complex but required for focus issues in safari, ie and opera
51913                     ed.row = row;
51914                     ed.col = col;
51915                     ed.record = r;
51916                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51917                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51918                     this.activeEditor = ed;
51919                     var v = r.data[field];
51920                     ed.startEdit(this.view.getCell(row, col), v);
51921                     // combo's with 'displayField and name set
51922                     if (ed.field.displayField && ed.field.name) {
51923                         ed.field.el.dom.value = r.data[ed.field.name];
51924                     }
51925                     
51926                     
51927                 }).defer(50, this);
51928             }
51929         }
51930     },
51931         
51932     /**
51933      * Stops any active editing
51934      */
51935     stopEditing : function(){
51936         if(this.activeEditor){
51937             this.activeEditor.completeEdit();
51938         }
51939         this.activeEditor = null;
51940     }
51941 });/*
51942  * Based on:
51943  * Ext JS Library 1.1.1
51944  * Copyright(c) 2006-2007, Ext JS, LLC.
51945  *
51946  * Originally Released Under LGPL - original licence link has changed is not relivant.
51947  *
51948  * Fork - LGPL
51949  * <script type="text/javascript">
51950  */
51951
51952 // private - not really -- you end up using it !
51953 // This is a support class used internally by the Grid components
51954
51955 /**
51956  * @class Roo.grid.GridEditor
51957  * @extends Roo.Editor
51958  * Class for creating and editable grid elements.
51959  * @param {Object} config any settings (must include field)
51960  */
51961 Roo.grid.GridEditor = function(field, config){
51962     if (!config && field.field) {
51963         config = field;
51964         field = Roo.factory(config.field, Roo.form);
51965     }
51966     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51967     field.monitorTab = false;
51968 };
51969
51970 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51971     
51972     /**
51973      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51974      */
51975     
51976     alignment: "tl-tl",
51977     autoSize: "width",
51978     hideEl : false,
51979     cls: "x-small-editor x-grid-editor",
51980     shim:false,
51981     shadow:"frame"
51982 });/*
51983  * Based on:
51984  * Ext JS Library 1.1.1
51985  * Copyright(c) 2006-2007, Ext JS, LLC.
51986  *
51987  * Originally Released Under LGPL - original licence link has changed is not relivant.
51988  *
51989  * Fork - LGPL
51990  * <script type="text/javascript">
51991  */
51992   
51993
51994   
51995 Roo.grid.PropertyRecord = Roo.data.Record.create([
51996     {name:'name',type:'string'},  'value'
51997 ]);
51998
51999
52000 Roo.grid.PropertyStore = function(grid, source){
52001     this.grid = grid;
52002     this.store = new Roo.data.Store({
52003         recordType : Roo.grid.PropertyRecord
52004     });
52005     this.store.on('update', this.onUpdate,  this);
52006     if(source){
52007         this.setSource(source);
52008     }
52009     Roo.grid.PropertyStore.superclass.constructor.call(this);
52010 };
52011
52012
52013
52014 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
52015     setSource : function(o){
52016         this.source = o;
52017         this.store.removeAll();
52018         var data = [];
52019         for(var k in o){
52020             if(this.isEditableValue(o[k])){
52021                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
52022             }
52023         }
52024         this.store.loadRecords({records: data}, {}, true);
52025     },
52026
52027     onUpdate : function(ds, record, type){
52028         if(type == Roo.data.Record.EDIT){
52029             var v = record.data['value'];
52030             var oldValue = record.modified['value'];
52031             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
52032                 this.source[record.id] = v;
52033                 record.commit();
52034                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
52035             }else{
52036                 record.reject();
52037             }
52038         }
52039     },
52040
52041     getProperty : function(row){
52042        return this.store.getAt(row);
52043     },
52044
52045     isEditableValue: function(val){
52046         if(val && val instanceof Date){
52047             return true;
52048         }else if(typeof val == 'object' || typeof val == 'function'){
52049             return false;
52050         }
52051         return true;
52052     },
52053
52054     setValue : function(prop, value){
52055         this.source[prop] = value;
52056         this.store.getById(prop).set('value', value);
52057     },
52058
52059     getSource : function(){
52060         return this.source;
52061     }
52062 });
52063
52064 Roo.grid.PropertyColumnModel = function(grid, store){
52065     this.grid = grid;
52066     var g = Roo.grid;
52067     g.PropertyColumnModel.superclass.constructor.call(this, [
52068         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
52069         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
52070     ]);
52071     this.store = store;
52072     this.bselect = Roo.DomHelper.append(document.body, {
52073         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
52074             {tag: 'option', value: 'true', html: 'true'},
52075             {tag: 'option', value: 'false', html: 'false'}
52076         ]
52077     });
52078     Roo.id(this.bselect);
52079     var f = Roo.form;
52080     this.editors = {
52081         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
52082         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
52083         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
52084         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
52085         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
52086     };
52087     this.renderCellDelegate = this.renderCell.createDelegate(this);
52088     this.renderPropDelegate = this.renderProp.createDelegate(this);
52089 };
52090
52091 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
52092     
52093     
52094     nameText : 'Name',
52095     valueText : 'Value',
52096     
52097     dateFormat : 'm/j/Y',
52098     
52099     
52100     renderDate : function(dateVal){
52101         return dateVal.dateFormat(this.dateFormat);
52102     },
52103
52104     renderBool : function(bVal){
52105         return bVal ? 'true' : 'false';
52106     },
52107
52108     isCellEditable : function(colIndex, rowIndex){
52109         return colIndex == 1;
52110     },
52111
52112     getRenderer : function(col){
52113         return col == 1 ?
52114             this.renderCellDelegate : this.renderPropDelegate;
52115     },
52116
52117     renderProp : function(v){
52118         return this.getPropertyName(v);
52119     },
52120
52121     renderCell : function(val){
52122         var rv = val;
52123         if(val instanceof Date){
52124             rv = this.renderDate(val);
52125         }else if(typeof val == 'boolean'){
52126             rv = this.renderBool(val);
52127         }
52128         return Roo.util.Format.htmlEncode(rv);
52129     },
52130
52131     getPropertyName : function(name){
52132         var pn = this.grid.propertyNames;
52133         return pn && pn[name] ? pn[name] : name;
52134     },
52135
52136     getCellEditor : function(colIndex, rowIndex){
52137         var p = this.store.getProperty(rowIndex);
52138         var n = p.data['name'], val = p.data['value'];
52139         
52140         if(typeof(this.grid.customEditors[n]) == 'string'){
52141             return this.editors[this.grid.customEditors[n]];
52142         }
52143         if(typeof(this.grid.customEditors[n]) != 'undefined'){
52144             return this.grid.customEditors[n];
52145         }
52146         if(val instanceof Date){
52147             return this.editors['date'];
52148         }else if(typeof val == 'number'){
52149             return this.editors['number'];
52150         }else if(typeof val == 'boolean'){
52151             return this.editors['boolean'];
52152         }else{
52153             return this.editors['string'];
52154         }
52155     }
52156 });
52157
52158 /**
52159  * @class Roo.grid.PropertyGrid
52160  * @extends Roo.grid.EditorGrid
52161  * This class represents the  interface of a component based property grid control.
52162  * <br><br>Usage:<pre><code>
52163  var grid = new Roo.grid.PropertyGrid("my-container-id", {
52164       
52165  });
52166  // set any options
52167  grid.render();
52168  * </code></pre>
52169   
52170  * @constructor
52171  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52172  * The container MUST have some type of size defined for the grid to fill. The container will be
52173  * automatically set to position relative if it isn't already.
52174  * @param {Object} config A config object that sets properties on this grid.
52175  */
52176 Roo.grid.PropertyGrid = function(container, config){
52177     config = config || {};
52178     var store = new Roo.grid.PropertyStore(this);
52179     this.store = store;
52180     var cm = new Roo.grid.PropertyColumnModel(this, store);
52181     store.store.sort('name', 'ASC');
52182     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
52183         ds: store.store,
52184         cm: cm,
52185         enableColLock:false,
52186         enableColumnMove:false,
52187         stripeRows:false,
52188         trackMouseOver: false,
52189         clicksToEdit:1
52190     }, config));
52191     this.getGridEl().addClass('x-props-grid');
52192     this.lastEditRow = null;
52193     this.on('columnresize', this.onColumnResize, this);
52194     this.addEvents({
52195          /**
52196              * @event beforepropertychange
52197              * Fires before a property changes (return false to stop?)
52198              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52199              * @param {String} id Record Id
52200              * @param {String} newval New Value
52201          * @param {String} oldval Old Value
52202              */
52203         "beforepropertychange": true,
52204         /**
52205              * @event propertychange
52206              * Fires after a property changes
52207              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52208              * @param {String} id Record Id
52209              * @param {String} newval New Value
52210          * @param {String} oldval Old Value
52211              */
52212         "propertychange": true
52213     });
52214     this.customEditors = this.customEditors || {};
52215 };
52216 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52217     
52218      /**
52219      * @cfg {Object} customEditors map of colnames=> custom editors.
52220      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52221      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52222      * false disables editing of the field.
52223          */
52224     
52225       /**
52226      * @cfg {Object} propertyNames map of property Names to their displayed value
52227          */
52228     
52229     render : function(){
52230         Roo.grid.PropertyGrid.superclass.render.call(this);
52231         this.autoSize.defer(100, this);
52232     },
52233
52234     autoSize : function(){
52235         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52236         if(this.view){
52237             this.view.fitColumns();
52238         }
52239     },
52240
52241     onColumnResize : function(){
52242         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52243         this.autoSize();
52244     },
52245     /**
52246      * Sets the data for the Grid
52247      * accepts a Key => Value object of all the elements avaiable.
52248      * @param {Object} data  to appear in grid.
52249      */
52250     setSource : function(source){
52251         this.store.setSource(source);
52252         //this.autoSize();
52253     },
52254     /**
52255      * Gets all the data from the grid.
52256      * @return {Object} data  data stored in grid
52257      */
52258     getSource : function(){
52259         return this.store.getSource();
52260     }
52261 });/*
52262  * Based on:
52263  * Ext JS Library 1.1.1
52264  * Copyright(c) 2006-2007, Ext JS, LLC.
52265  *
52266  * Originally Released Under LGPL - original licence link has changed is not relivant.
52267  *
52268  * Fork - LGPL
52269  * <script type="text/javascript">
52270  */
52271  
52272 /**
52273  * @class Roo.LoadMask
52274  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52275  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52276  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52277  * element's UpdateManager load indicator and will be destroyed after the initial load.
52278  * @constructor
52279  * Create a new LoadMask
52280  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52281  * @param {Object} config The config object
52282  */
52283 Roo.LoadMask = function(el, config){
52284     this.el = Roo.get(el);
52285     Roo.apply(this, config);
52286     if(this.store){
52287         this.store.on('beforeload', this.onBeforeLoad, this);
52288         this.store.on('load', this.onLoad, this);
52289         this.store.on('loadexception', this.onLoadException, this);
52290         this.removeMask = false;
52291     }else{
52292         var um = this.el.getUpdateManager();
52293         um.showLoadIndicator = false; // disable the default indicator
52294         um.on('beforeupdate', this.onBeforeLoad, this);
52295         um.on('update', this.onLoad, this);
52296         um.on('failure', this.onLoad, this);
52297         this.removeMask = true;
52298     }
52299 };
52300
52301 Roo.LoadMask.prototype = {
52302     /**
52303      * @cfg {Boolean} removeMask
52304      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52305      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52306      */
52307     /**
52308      * @cfg {String} msg
52309      * The text to display in a centered loading message box (defaults to 'Loading...')
52310      */
52311     msg : 'Loading...',
52312     /**
52313      * @cfg {String} msgCls
52314      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52315      */
52316     msgCls : 'x-mask-loading',
52317
52318     /**
52319      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52320      * @type Boolean
52321      */
52322     disabled: false,
52323
52324     /**
52325      * Disables the mask to prevent it from being displayed
52326      */
52327     disable : function(){
52328        this.disabled = true;
52329     },
52330
52331     /**
52332      * Enables the mask so that it can be displayed
52333      */
52334     enable : function(){
52335         this.disabled = false;
52336     },
52337     
52338     onLoadException : function()
52339     {
52340         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52341             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52342         }
52343         this.el.unmask(this.removeMask);
52344     },
52345     // private
52346     onLoad : function()
52347     {
52348         this.el.unmask(this.removeMask);
52349     },
52350
52351     // private
52352     onBeforeLoad : function(){
52353         if(!this.disabled){
52354             this.el.mask(this.msg, this.msgCls);
52355         }
52356     },
52357
52358     // private
52359     destroy : function(){
52360         if(this.store){
52361             this.store.un('beforeload', this.onBeforeLoad, this);
52362             this.store.un('load', this.onLoad, this);
52363             this.store.un('loadexception', this.onLoadException, this);
52364         }else{
52365             var um = this.el.getUpdateManager();
52366             um.un('beforeupdate', this.onBeforeLoad, this);
52367             um.un('update', this.onLoad, this);
52368             um.un('failure', this.onLoad, this);
52369         }
52370     }
52371 };/*
52372  * Based on:
52373  * Ext JS Library 1.1.1
52374  * Copyright(c) 2006-2007, Ext JS, LLC.
52375  *
52376  * Originally Released Under LGPL - original licence link has changed is not relivant.
52377  *
52378  * Fork - LGPL
52379  * <script type="text/javascript">
52380  */
52381
52382
52383 /**
52384  * @class Roo.XTemplate
52385  * @extends Roo.Template
52386  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
52387 <pre><code>
52388 var t = new Roo.XTemplate(
52389         '&lt;select name="{name}"&gt;',
52390                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
52391         '&lt;/select&gt;'
52392 );
52393  
52394 // then append, applying the master template values
52395  </code></pre>
52396  *
52397  * Supported features:
52398  *
52399  *  Tags:
52400
52401 <pre><code>
52402       {a_variable} - output encoded.
52403       {a_variable.format:("Y-m-d")} - call a method on the variable
52404       {a_variable:raw} - unencoded output
52405       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
52406       {a_variable:this.method_on_template(...)} - call a method on the template object.
52407  
52408 </code></pre>
52409  *  The tpl tag:
52410 <pre><code>
52411         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
52412         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
52413         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
52414         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
52415   
52416         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
52417         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
52418 </code></pre>
52419  *      
52420  */
52421 Roo.XTemplate = function()
52422 {
52423     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52424     if (this.html) {
52425         this.compile();
52426     }
52427 };
52428
52429
52430 Roo.extend(Roo.XTemplate, Roo.Template, {
52431
52432     /**
52433      * The various sub templates
52434      */
52435     tpls : false,
52436     /**
52437      *
52438      * basic tag replacing syntax
52439      * WORD:WORD()
52440      *
52441      * // you can fake an object call by doing this
52442      *  x.t:(test,tesT) 
52443      * 
52444      */
52445     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52446
52447     /**
52448      * compile the template
52449      *
52450      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
52451      *
52452      */
52453     compile: function()
52454     {
52455         var s = this.html;
52456      
52457         s = ['<tpl>', s, '</tpl>'].join('');
52458     
52459         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
52460             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
52461             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
52462             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
52463             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
52464             m,
52465             id     = 0,
52466             tpls   = [];
52467     
52468         while(true == !!(m = s.match(re))){
52469             var forMatch   = m[0].match(nameRe),
52470                 ifMatch   = m[0].match(ifRe),
52471                 execMatch   = m[0].match(execRe),
52472                 namedMatch   = m[0].match(namedRe),
52473                 
52474                 exp  = null, 
52475                 fn   = null,
52476                 exec = null,
52477                 name = forMatch && forMatch[1] ? forMatch[1] : '';
52478                 
52479             if (ifMatch) {
52480                 // if - puts fn into test..
52481                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
52482                 if(exp){
52483                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52484                 }
52485             }
52486             
52487             if (execMatch) {
52488                 // exec - calls a function... returns empty if true is  returned.
52489                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
52490                 if(exp){
52491                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52492                 }
52493             }
52494             
52495             
52496             if (name) {
52497                 // for = 
52498                 switch(name){
52499                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52500                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52501                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52502                 }
52503             }
52504             var uid = namedMatch ? namedMatch[1] : id;
52505             
52506             
52507             tpls.push({
52508                 id:     namedMatch ? namedMatch[1] : id,
52509                 target: name,
52510                 exec:   exec,
52511                 test:   fn,
52512                 body:   m[1] || ''
52513             });
52514             if (namedMatch) {
52515                 s = s.replace(m[0], '');
52516             } else { 
52517                 s = s.replace(m[0], '{xtpl'+ id + '}');
52518             }
52519             ++id;
52520         }
52521         this.tpls = [];
52522         for(var i = tpls.length-1; i >= 0; --i){
52523             this.compileTpl(tpls[i]);
52524             this.tpls[tpls[i].id] = tpls[i];
52525         }
52526         this.master = tpls[tpls.length-1];
52527         return this;
52528     },
52529     /**
52530      * same as applyTemplate, except it's done to one of the subTemplates
52531      * when using named templates, you can do:
52532      *
52533      * var str = pl.applySubTemplate('your-name', values);
52534      *
52535      * 
52536      * @param {Number} id of the template
52537      * @param {Object} values to apply to template
52538      * @param {Object} parent (normaly the instance of this object)
52539      */
52540     applySubTemplate : function(id, values, parent)
52541     {
52542         
52543         
52544         var t = this.tpls[id];
52545         
52546         
52547         try { 
52548             if(t.test && !t.test.call(this, values, parent)){
52549                 return '';
52550             }
52551         } catch(e) {
52552             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
52553             Roo.log(e.toString());
52554             Roo.log(t.test);
52555             return ''
52556         }
52557         try { 
52558             
52559             if(t.exec && t.exec.call(this, values, parent)){
52560                 return '';
52561             }
52562         } catch(e) {
52563             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
52564             Roo.log(e.toString());
52565             Roo.log(t.exec);
52566             return ''
52567         }
52568         try {
52569             var vs = t.target ? t.target.call(this, values, parent) : values;
52570             parent = t.target ? values : parent;
52571             if(t.target && vs instanceof Array){
52572                 var buf = [];
52573                 for(var i = 0, len = vs.length; i < len; i++){
52574                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
52575                 }
52576                 return buf.join('');
52577             }
52578             return t.compiled.call(this, vs, parent);
52579         } catch (e) {
52580             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
52581             Roo.log(e.toString());
52582             Roo.log(t.compiled);
52583             return '';
52584         }
52585     },
52586
52587     compileTpl : function(tpl)
52588     {
52589         var fm = Roo.util.Format;
52590         var useF = this.disableFormats !== true;
52591         var sep = Roo.isGecko ? "+" : ",";
52592         var undef = function(str) {
52593             Roo.log("Property not found :"  + str);
52594             return '';
52595         };
52596         
52597         var fn = function(m, name, format, args)
52598         {
52599             //Roo.log(arguments);
52600             args = args ? args.replace(/\\'/g,"'") : args;
52601             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
52602             if (typeof(format) == 'undefined') {
52603                 format= 'htmlEncode';
52604             }
52605             if (format == 'raw' ) {
52606                 format = false;
52607             }
52608             
52609             if(name.substr(0, 4) == 'xtpl'){
52610                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52611             }
52612             
52613             // build an array of options to determine if value is undefined..
52614             
52615             // basically get 'xxxx.yyyy' then do
52616             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
52617             //    (function () { Roo.log("Property not found"); return ''; })() :
52618             //    ......
52619             
52620             var udef_ar = [];
52621             var lookfor = '';
52622             Roo.each(name.split('.'), function(st) {
52623                 lookfor += (lookfor.length ? '.': '') + st;
52624                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
52625             });
52626             
52627             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
52628             
52629             
52630             if(format && useF){
52631                 
52632                 args = args ? ',' + args : "";
52633                  
52634                 if(format.substr(0, 5) != "this."){
52635                     format = "fm." + format + '(';
52636                 }else{
52637                     format = 'this.call("'+ format.substr(5) + '", ';
52638                     args = ", values";
52639                 }
52640                 
52641                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
52642             }
52643              
52644             if (args.length) {
52645                 // called with xxyx.yuu:(test,test)
52646                 // change to ()
52647                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
52648             }
52649             // raw.. - :raw modifier..
52650             return "'"+ sep + udef_st  + name + ")"+sep+"'";
52651             
52652         };
52653         var body;
52654         // branched to use + in gecko and [].join() in others
52655         if(Roo.isGecko){
52656             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
52657                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52658                     "';};};";
52659         }else{
52660             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
52661             body.push(tpl.body.replace(/(\r\n|\n)/g,
52662                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52663             body.push("'].join('');};};");
52664             body = body.join('');
52665         }
52666         
52667         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
52668        
52669         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
52670         eval(body);
52671         
52672         return this;
52673     },
52674
52675     applyTemplate : function(values){
52676         return this.master.compiled.call(this, values, {});
52677         //var s = this.subs;
52678     },
52679
52680     apply : function(){
52681         return this.applyTemplate.apply(this, arguments);
52682     }
52683
52684  });
52685
52686 Roo.XTemplate.from = function(el){
52687     el = Roo.getDom(el);
52688     return new Roo.XTemplate(el.value || el.innerHTML);
52689 };/*
52690  * Original code for Roojs - LGPL
52691  * <script type="text/javascript">
52692  */
52693  
52694 /**
52695  * @class Roo.XComponent
52696  * A delayed Element creator...
52697  * Or a way to group chunks of interface together.
52698  * 
52699  * Mypart.xyx = new Roo.XComponent({
52700
52701     parent : 'Mypart.xyz', // empty == document.element.!!
52702     order : '001',
52703     name : 'xxxx'
52704     region : 'xxxx'
52705     disabled : function() {} 
52706      
52707     tree : function() { // return an tree of xtype declared components
52708         var MODULE = this;
52709         return 
52710         {
52711             xtype : 'NestedLayoutPanel',
52712             // technicall
52713         }
52714      ]
52715  *})
52716  *
52717  *
52718  * It can be used to build a big heiracy, with parent etc.
52719  * or you can just use this to render a single compoent to a dom element
52720  * MYPART.render(Roo.Element | String(id) | dom_element )
52721  * 
52722  * @extends Roo.util.Observable
52723  * @constructor
52724  * @param cfg {Object} configuration of component
52725  * 
52726  */
52727 Roo.XComponent = function(cfg) {
52728     Roo.apply(this, cfg);
52729     this.addEvents({ 
52730         /**
52731              * @event built
52732              * Fires when this the componnt is built
52733              * @param {Roo.XComponent} c the component
52734              */
52735         'built' : true
52736         
52737     });
52738     this.region = this.region || 'center'; // default..
52739     Roo.XComponent.register(this);
52740     this.modules = false;
52741     this.el = false; // where the layout goes..
52742     
52743     
52744 }
52745 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52746     /**
52747      * @property el
52748      * The created element (with Roo.factory())
52749      * @type {Roo.Layout}
52750      */
52751     el  : false,
52752     
52753     /**
52754      * @property el
52755      * for BC  - use el in new code
52756      * @type {Roo.Layout}
52757      */
52758     panel : false,
52759     
52760     /**
52761      * @property layout
52762      * for BC  - use el in new code
52763      * @type {Roo.Layout}
52764      */
52765     layout : false,
52766     
52767      /**
52768      * @cfg {Function|boolean} disabled
52769      * If this module is disabled by some rule, return true from the funtion
52770      */
52771     disabled : false,
52772     
52773     /**
52774      * @cfg {String} parent 
52775      * Name of parent element which it get xtype added to..
52776      */
52777     parent: false,
52778     
52779     /**
52780      * @cfg {String} order
52781      * Used to set the order in which elements are created (usefull for multiple tabs)
52782      */
52783     
52784     order : false,
52785     /**
52786      * @cfg {String} name
52787      * String to display while loading.
52788      */
52789     name : false,
52790     /**
52791      * @cfg {String} region
52792      * Region to render component to (defaults to center)
52793      */
52794     region : 'center',
52795     
52796     /**
52797      * @cfg {Array} items
52798      * A single item array - the first element is the root of the tree..
52799      * It's done this way to stay compatible with the Xtype system...
52800      */
52801     items : false,
52802     
52803     /**
52804      * @property _tree
52805      * The method that retuns the tree of parts that make up this compoennt 
52806      * @type {function}
52807      */
52808     _tree  : false,
52809     
52810      /**
52811      * render
52812      * render element to dom or tree
52813      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52814      */
52815     
52816     render : function(el)
52817     {
52818         
52819         el = el || false;
52820         var hp = this.parent ? 1 : 0;
52821         
52822         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52823             // if parent is a '#.....' string, then let's use that..
52824             var ename = this.parent.substr(1)
52825             this.parent = false;
52826             el = Roo.get(ename);
52827             if (!el) {
52828                 Roo.log("Warning - element can not be found :#" + ename );
52829                 return;
52830             }
52831         }
52832         
52833         
52834         if (!this.parent) {
52835             
52836             el = el ? Roo.get(el) : false;      
52837             
52838             // it's a top level one..
52839             this.parent =  {
52840                 el : new Roo.BorderLayout(el || document.body, {
52841                 
52842                      center: {
52843                          titlebar: false,
52844                          autoScroll:false,
52845                          closeOnTab: true,
52846                          tabPosition: 'top',
52847                           //resizeTabs: true,
52848                          alwaysShowTabs: el && hp? false :  true,
52849                          hideTabs: el || !hp ? true :  false,
52850                          minTabWidth: 140
52851                      }
52852                  })
52853             }
52854         }
52855         
52856                 
52857                 // The 'tree' method is  '_tree now' 
52858             
52859         var tree = this._tree ? this._tree() : this.tree();
52860         tree.region = tree.region || this.region;
52861         this.el = this.parent.el.addxtype(tree);
52862         this.fireEvent('built', this);
52863         
52864         this.panel = this.el;
52865         this.layout = this.panel.layout;
52866                 this.parentLayout = this.parent.layout  || false;  
52867          
52868     }
52869     
52870 });
52871
52872 Roo.apply(Roo.XComponent, {
52873     
52874     /**
52875      * @property  buildCompleted
52876      * True when the builder has completed building the interface.
52877      * @type Boolean
52878      */
52879     buildCompleted : false,
52880      
52881     /**
52882      * @property  topModule
52883      * the upper most module - uses document.element as it's constructor.
52884      * @type Object
52885      */
52886      
52887     topModule  : false,
52888       
52889     /**
52890      * @property  modules
52891      * array of modules to be created by registration system.
52892      * @type {Array} of Roo.XComponent
52893      */
52894     
52895     modules : [],
52896     /**
52897      * @property  elmodules
52898      * array of modules to be created by which use #ID 
52899      * @type {Array} of Roo.XComponent
52900      */
52901      
52902     elmodules : [],
52903
52904     
52905     /**
52906      * Register components to be built later.
52907      *
52908      * This solves the following issues
52909      * - Building is not done on page load, but after an authentication process has occured.
52910      * - Interface elements are registered on page load
52911      * - Parent Interface elements may not be loaded before child, so this handles that..
52912      * 
52913      *
52914      * example:
52915      * 
52916      * MyApp.register({
52917           order : '000001',
52918           module : 'Pman.Tab.projectMgr',
52919           region : 'center',
52920           parent : 'Pman.layout',
52921           disabled : false,  // or use a function..
52922         })
52923      
52924      * * @param {Object} details about module
52925      */
52926     register : function(obj) {
52927                 
52928                 Roo.XComponent.event.fireEvent('register', obj);
52929                 switch(typeof(obj.disabled) ) {
52930                         
52931                         case 'undefined':
52932                                 break;
52933                         
52934                         case 'function':
52935                                 if ( obj.disabled() ) {
52936                                         return;
52937                                 }
52938                                 break;
52939                         default:
52940                                 if (obj.disabled) {
52941                                         return;
52942                                 }
52943                                 break;
52944                 }
52945                 
52946         this.modules.push(obj);
52947          
52948     },
52949     /**
52950      * convert a string to an object..
52951      * eg. 'AAA.BBB' -> finds AAA.BBB
52952
52953      */
52954     
52955     toObject : function(str)
52956     {
52957         if (!str || typeof(str) == 'object') {
52958             return str;
52959         }
52960         if (str.substring(0,1) == '#') {
52961             return str;
52962         }
52963
52964         var ar = str.split('.');
52965         var rt, o;
52966         rt = ar.shift();
52967             /** eval:var:o */
52968         try {
52969             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52970         } catch (e) {
52971             throw "Module not found : " + str;
52972         }
52973         
52974         if (o === false) {
52975             throw "Module not found : " + str;
52976         }
52977         Roo.each(ar, function(e) {
52978             if (typeof(o[e]) == 'undefined') {
52979                 throw "Module not found : " + str;
52980             }
52981             o = o[e];
52982         });
52983         
52984         return o;
52985         
52986     },
52987     
52988     
52989     /**
52990      * move modules into their correct place in the tree..
52991      * 
52992      */
52993     preBuild : function ()
52994     {
52995         var _t = this;
52996         Roo.each(this.modules , function (obj)
52997         {
52998             var opar = obj.parent;
52999             try { 
53000                 obj.parent = this.toObject(opar);
53001             } catch(e) {
53002                 Roo.log("parent:toObject failed: " + e.toString());
53003                 return;
53004             }
53005             
53006             if (!obj.parent) {
53007                                 Roo.debug && Roo.log("GOT top level module");
53008                                 Roo.debug && Roo.log(obj);
53009                                 obj.modules = new Roo.util.MixedCollection(false, 
53010                     function(o) { return o.order + '' }
53011                 );
53012                 this.topModule = obj;
53013                 return;
53014             }
53015                         // parent is a string (usually a dom element name..)
53016             if (typeof(obj.parent) == 'string') {
53017                 this.elmodules.push(obj);
53018                 return;
53019             }
53020             if (obj.parent.constructor != Roo.XComponent) {
53021                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
53022             }
53023             if (!obj.parent.modules) {
53024                 obj.parent.modules = new Roo.util.MixedCollection(false, 
53025                     function(o) { return o.order + '' }
53026                 );
53027             }
53028             
53029             obj.parent.modules.add(obj);
53030         }, this);
53031     },
53032     
53033      /**
53034      * make a list of modules to build.
53035      * @return {Array} list of modules. 
53036      */ 
53037     
53038     buildOrder : function()
53039     {
53040         var _this = this;
53041         var cmp = function(a,b) {   
53042             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
53043         };
53044         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
53045             throw "No top level modules to build";
53046         }
53047         
53048         // make a flat list in order of modules to build.
53049         var mods = this.topModule ? [ this.topModule ] : [];
53050                 
53051                 // elmodules (is a list of DOM based modules )
53052         Roo.each(this.elmodules,function(e) { mods.push(e) });
53053
53054         
53055         // add modules to their parents..
53056         var addMod = function(m) {
53057                         Roo.debug && Roo.log("build Order: add: " + m.name);
53058             
53059             mods.push(m);
53060             if (m.modules) {
53061                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
53062                 m.modules.keySort('ASC',  cmp );
53063                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
53064
53065                 m.modules.each(addMod);
53066             } else {
53067                                 Roo.debug && Roo.log("build Order: no child modules");
53068                         }
53069             // not sure if this is used any more..
53070             if (m.finalize) {
53071                 m.finalize.name = m.name + " (clean up) ";
53072                 mods.push(m.finalize);
53073             }
53074             
53075         }
53076         if (this.topModule) { 
53077             this.topModule.modules.keySort('ASC',  cmp );
53078             this.topModule.modules.each(addMod);
53079         }
53080         return mods;
53081     },
53082     
53083      /**
53084      * Build the registered modules.
53085      * @param {Object} parent element.
53086      * @param {Function} optional method to call after module has been added.
53087      * 
53088      */ 
53089    
53090     build : function() 
53091     {
53092         
53093         this.preBuild();
53094         var mods = this.buildOrder();
53095       
53096         //this.allmods = mods;
53097         //Roo.debug && Roo.log(mods);
53098         //return;
53099         if (!mods.length) { // should not happen
53100             throw "NO modules!!!";
53101         }
53102         
53103         
53104         var msg = "Building Interface...";
53105         // flash it up as modal - so we store the mask!?
53106         Roo.MessageBox.show({ title: 'loading' });
53107         Roo.MessageBox.show({
53108            title: "Please wait...",
53109            msg: msg,
53110            width:450,
53111            progress:true,
53112            closable:false,
53113            modal: false
53114           
53115         });
53116         var total = mods.length;
53117         
53118         var _this = this;
53119         var progressRun = function() {
53120             if (!mods.length) {
53121                 Roo.debug && Roo.log('hide?');
53122                 Roo.MessageBox.hide();
53123                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
53124                 
53125                 // THE END...
53126                 return false;   
53127             }
53128             
53129             var m = mods.shift();
53130             
53131             
53132             Roo.debug && Roo.log(m);
53133             // not sure if this is supported any more.. - modules that are are just function
53134             if (typeof(m) == 'function') { 
53135                 m.call(this);
53136                 return progressRun.defer(10, _this);
53137             } 
53138             
53139             
53140             msg = "Building Interface " + (total  - mods.length) + 
53141                     " of " + total + 
53142                     (m.name ? (' - ' + m.name) : '');
53143                         Roo.debug && Roo.log(msg);
53144             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
53145             
53146          
53147             // is the module disabled?
53148             var disabled = (typeof(m.disabled) == 'function') ?
53149                 m.disabled.call(m.module.disabled) : m.disabled;    
53150             
53151             
53152             if (disabled) {
53153                 return progressRun(); // we do not update the display!
53154             }
53155             
53156             // now build 
53157             
53158                         
53159                         
53160             m.render();
53161             // it's 10 on top level, and 1 on others??? why...
53162             return progressRun.defer(10, _this);
53163              
53164         }
53165         progressRun.defer(1, _this);
53166      
53167         
53168         
53169     },
53170         
53171         
53172         /**
53173          * Event Object.
53174          *
53175          *
53176          */
53177         event: false, 
53178     /**
53179          * wrapper for event.on - aliased later..  
53180          * Typically use to register a event handler for register:
53181          *
53182          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
53183          *
53184          */
53185     on : false
53186    
53187     
53188     
53189 });
53190
53191 Roo.XComponent.event = new Roo.util.Observable({
53192                 events : { 
53193                         /**
53194                          * @event register
53195                          * Fires when an Component is registered,
53196                          * set the disable property on the Component to stop registration.
53197                          * @param {Roo.XComponent} c the component being registerd.
53198                          * 
53199                          */
53200                         'register' : true,
53201                         /**
53202                          * @event buildcomplete
53203                          * Fires on the top level element when all elements have been built
53204                          * @param {Roo.XComponent} the top level component.
53205                          */
53206                         'buildcomplete' : true
53207                         
53208                 }
53209 });
53210
53211 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
53212  //<script type="text/javascript">
53213
53214
53215 /**
53216  * @class Roo.Login
53217  * @extends Roo.LayoutDialog
53218  * A generic Login Dialog..... - only one needed in theory!?!?
53219  *
53220  * Fires XComponent builder on success...
53221  * 
53222  * Sends 
53223  *    username,password, lang = for login actions.
53224  *    check = 1 for periodic checking that sesion is valid.
53225  *    passwordRequest = email request password
53226  *    logout = 1 = to logout
53227  * 
53228  * Affects: (this id="????" elements)
53229  *   loading  (removed) (used to indicate application is loading)
53230  *   loading-mask (hides) (used to hide application when it's building loading)
53231  *   
53232  * 
53233  * Usage: 
53234  *    
53235  * 
53236  * Myapp.login = Roo.Login({
53237      url: xxxx,
53238    
53239      realm : 'Myapp', 
53240      
53241      
53242      method : 'POST',
53243      
53244      
53245      * 
53246  })
53247  * 
53248  * 
53249  * 
53250  **/
53251  
53252 Roo.Login = function(cfg)
53253 {
53254     this.addEvents({
53255         'refreshed' : true
53256     });
53257     
53258     Roo.apply(this,cfg);
53259     
53260     Roo.onReady(function() {
53261         this.onLoad();
53262     }, this);
53263     // call parent..
53264     
53265    
53266     Roo.Login.superclass.constructor.call(this, this);
53267     //this.addxtype(this.items[0]);
53268     
53269     
53270 }
53271
53272
53273 Roo.extend(Roo.Login, Roo.LayoutDialog, {
53274     
53275     /**
53276      * @cfg {String} method
53277      * Method used to query for login details.
53278      */
53279     
53280     method : 'POST',
53281     /**
53282      * @cfg {String} url
53283      * URL to query login data. - eg. baseURL + '/Login.php'
53284      */
53285     url : '',
53286     
53287     /**
53288      * @property user
53289      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
53290      * @type {Object} 
53291      */
53292     user : false,
53293     /**
53294      * @property checkFails
53295      * Number of times we have attempted to get authentication check, and failed.
53296      * @type {Number} 
53297      */
53298     checkFails : 0,
53299       /**
53300      * @property intervalID
53301      * The window interval that does the constant login checking.
53302      * @type {Number} 
53303      */
53304     intervalID : 0,
53305     
53306     
53307     onLoad : function() // called on page load...
53308     {
53309         // load 
53310          
53311         if (Roo.get('loading')) { // clear any loading indicator..
53312             Roo.get('loading').remove();
53313         }
53314         
53315         //this.switchLang('en'); // set the language to english..
53316        
53317         this.check({
53318             success:  function(response, opts)  {  // check successfull...
53319             
53320                 var res = this.processResponse(response);
53321                 this.checkFails =0;
53322                 if (!res.success) { // error!
53323                     this.checkFails = 5;
53324                     //console.log('call failure');
53325                     return this.failure(response,opts);
53326                 }
53327                 
53328                 if (!res.data.id) { // id=0 == login failure.
53329                     return this.show();
53330                 }
53331                 
53332                               
53333                         //console.log(success);
53334                 this.fillAuth(res.data);   
53335                 this.checkFails =0;
53336                 Roo.XComponent.build();
53337             },
53338             failure : this.show
53339         });
53340         
53341     }, 
53342     
53343     
53344     check: function(cfg) // called every so often to refresh cookie etc..
53345     {
53346         if (cfg.again) { // could be undefined..
53347             this.checkFails++;
53348         } else {
53349             this.checkFails = 0;
53350         }
53351         var _this = this;
53352         if (this.sending) {
53353             if ( this.checkFails > 4) {
53354                 Roo.MessageBox.alert("Error",  
53355                     "Error getting authentication status. - try reloading, or wait a while", function() {
53356                         _this.sending = false;
53357                     }); 
53358                 return;
53359             }
53360             cfg.again = true;
53361             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
53362             return;
53363         }
53364         this.sending = true;
53365         
53366         Roo.Ajax.request({  
53367             url: this.url,
53368             params: {
53369                 getAuthUser: true
53370             },  
53371             method: this.method,
53372             success:  cfg.success || this.success,
53373             failure : cfg.failure || this.failure,
53374             scope : this,
53375             callCfg : cfg
53376               
53377         });  
53378     }, 
53379     
53380     
53381     logout: function()
53382     {
53383         window.onbeforeunload = function() { }; // false does not work for IE..
53384         this.user = false;
53385         var _this = this;
53386         
53387         Roo.Ajax.request({  
53388             url: this.url,
53389             params: {
53390                 logout: 1
53391             },  
53392             method: 'GET',
53393             failure : function() {
53394                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
53395                     document.location = document.location.toString() + '?ts=' + Math.random();
53396                 });
53397                 
53398             },
53399             success : function() {
53400                 _this.user = false;
53401                 this.checkFails =0;
53402                 // fixme..
53403                 document.location = document.location.toString() + '?ts=' + Math.random();
53404             }
53405               
53406               
53407         }); 
53408     },
53409     
53410     processResponse : function (response)
53411     {
53412         var res = '';
53413         try {
53414             res = Roo.decode(response.responseText);
53415             // oops...
53416             if (typeof(res) != 'object') {
53417                 res = { success : false, errorMsg : res, errors : true };
53418             }
53419             if (typeof(res.success) == 'undefined') {
53420                 res.success = false;
53421             }
53422             
53423         } catch(e) {
53424             res = { success : false,  errorMsg : response.responseText, errors : true };
53425         }
53426         return res;
53427     },
53428     
53429     success : function(response, opts)  // check successfull...
53430     {  
53431         this.sending = false;
53432         var res = this.processResponse(response);
53433         if (!res.success) {
53434             return this.failure(response, opts);
53435         }
53436         if (!res.data || !res.data.id) {
53437             return this.failure(response,opts);
53438         }
53439         //console.log(res);
53440         this.fillAuth(res.data);
53441         
53442         this.checkFails =0;
53443         
53444     },
53445     
53446     
53447     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53448     {
53449         this.authUser = -1;
53450         this.sending = false;
53451         var res = this.processResponse(response);
53452         //console.log(res);
53453         if ( this.checkFails > 2) {
53454         
53455             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53456                 "Error getting authentication status. - try reloading"); 
53457             return;
53458         }
53459         opts.callCfg.again = true;
53460         this.check.defer(1000, this, [ opts.callCfg ]);
53461         return;  
53462     },
53463     
53464     
53465     
53466     fillAuth: function(au) {
53467         this.startAuthCheck();
53468         this.authUserId = au.id;
53469         this.authUser = au;
53470         this.lastChecked = new Date();
53471         this.fireEvent('refreshed', au);
53472         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53473         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53474         au.lang = au.lang || 'en';
53475         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53476         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53477         this.switchLang(au.lang );
53478         
53479      
53480         // open system... - -on setyp..
53481         if (this.authUserId  < 0) {
53482             Roo.MessageBox.alert("Warning", 
53483                 "This is an open system - please set up a admin user with a password.");  
53484         }
53485          
53486         //Pman.onload(); // which should do nothing if it's a re-auth result...
53487         
53488              
53489     },
53490     
53491     startAuthCheck : function() // starter for timeout checking..
53492     {
53493         if (this.intervalID) { // timer already in place...
53494             return false;
53495         }
53496         var _this = this;
53497         this.intervalID =  window.setInterval(function() {
53498               _this.check(false);
53499             }, 120000); // every 120 secs = 2mins..
53500         
53501         
53502     },
53503          
53504     
53505     switchLang : function (lang) 
53506     {
53507         _T = typeof(_T) == 'undefined' ? false : _T;
53508           if (!_T || !lang.length) {
53509             return;
53510         }
53511         
53512         if (!_T && lang != 'en') {
53513             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53514             return;
53515         }
53516         
53517         if (typeof(_T.en) == 'undefined') {
53518             _T.en = {};
53519             Roo.apply(_T.en, _T);
53520         }
53521         
53522         if (typeof(_T[lang]) == 'undefined') {
53523             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53524             return;
53525         }
53526         
53527         
53528         Roo.apply(_T, _T[lang]);
53529         // just need to set the text values for everything...
53530         var _this = this;
53531         /* this will not work ...
53532         if (this.form) { 
53533             
53534                
53535             function formLabel(name, val) {
53536                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53537             }
53538             
53539             formLabel('password', "Password"+':');
53540             formLabel('username', "Email Address"+':');
53541             formLabel('lang', "Language"+':');
53542             this.dialog.setTitle("Login");
53543             this.dialog.buttons[0].setText("Forgot Password");
53544             this.dialog.buttons[1].setText("Login");
53545         }
53546         */
53547         
53548         
53549     },
53550     
53551     
53552     title: "Login",
53553     modal: true,
53554     width:  350,
53555     //height: 230,
53556     height: 180,
53557     shadow: true,
53558     minWidth:200,
53559     minHeight:180,
53560     //proxyDrag: true,
53561     closable: false,
53562     draggable: false,
53563     collapsible: false,
53564     resizable: false,
53565     center: {  // needed??
53566         autoScroll:false,
53567         titlebar: false,
53568        // tabPosition: 'top',
53569         hideTabs: true,
53570         closeOnTab: true,
53571         alwaysShowTabs: false
53572     } ,
53573     listeners : {
53574         
53575         show  : function(dlg)
53576         {
53577             //console.log(this);
53578             this.form = this.layout.getRegion('center').activePanel.form;
53579             this.form.dialog = dlg;
53580             this.buttons[0].form = this.form;
53581             this.buttons[0].dialog = dlg;
53582             this.buttons[1].form = this.form;
53583             this.buttons[1].dialog = dlg;
53584            
53585            //this.resizeToLogo.defer(1000,this);
53586             // this is all related to resizing for logos..
53587             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53588            //// if (!sz) {
53589              //   this.resizeToLogo.defer(1000,this);
53590              //   return;
53591            // }
53592             //var w = Ext.lib.Dom.getViewWidth() - 100;
53593             //var h = Ext.lib.Dom.getViewHeight() - 100;
53594             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53595             //this.center();
53596             if (this.disabled) {
53597                 this.hide();
53598                 return;
53599             }
53600             
53601             if (this.user.id < 0) { // used for inital setup situations.
53602                 return;
53603             }
53604             
53605             if (this.intervalID) {
53606                 // remove the timer
53607                 window.clearInterval(this.intervalID);
53608                 this.intervalID = false;
53609             }
53610             
53611             
53612             if (Roo.get('loading')) {
53613                 Roo.get('loading').remove();
53614             }
53615             if (Roo.get('loading-mask')) {
53616                 Roo.get('loading-mask').hide();
53617             }
53618             
53619             //incomming._node = tnode;
53620             this.form.reset();
53621             //this.dialog.modal = !modal;
53622             //this.dialog.show();
53623             this.el.unmask(); 
53624             
53625             
53626             this.form.setValues({
53627                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53628                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53629             });
53630             
53631             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53632             if (this.form.findField('username').getValue().length > 0 ){
53633                 this.form.findField('password').focus();
53634             } else {
53635                this.form.findField('username').focus();
53636             }
53637     
53638         }
53639     },
53640     items : [
53641          {
53642        
53643             xtype : 'ContentPanel',
53644             xns : Roo,
53645             region: 'center',
53646             fitToFrame : true,
53647             
53648             items : [
53649     
53650                 {
53651                
53652                     xtype : 'Form',
53653                     xns : Roo.form,
53654                     labelWidth: 100,
53655                     style : 'margin: 10px;',
53656                     
53657                     listeners : {
53658                         actionfailed : function(f, act) {
53659                             // form can return { errors: .... }
53660                                 
53661                             //act.result.errors // invalid form element list...
53662                             //act.result.errorMsg// invalid form element list...
53663                             
53664                             this.dialog.el.unmask();
53665                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53666                                         "Login failed - communication error - try again.");
53667                                       
53668                         },
53669                         actioncomplete: function(re, act) {
53670                              
53671                             Roo.state.Manager.set(
53672                                 this.dialog.realm + '.username',  
53673                                     this.findField('username').getValue()
53674                             );
53675                             Roo.state.Manager.set(
53676                                 this.dialog.realm + '.lang',  
53677                                 this.findField('lang').getValue() 
53678                             );
53679                             
53680                             this.dialog.fillAuth(act.result.data);
53681                               
53682                             this.dialog.hide();
53683                             
53684                             if (Roo.get('loading-mask')) {
53685                                 Roo.get('loading-mask').show();
53686                             }
53687                             Roo.XComponent.build();
53688                             
53689                              
53690                             
53691                         }
53692                     },
53693                     items : [
53694                         {
53695                             xtype : 'TextField',
53696                             xns : Roo.form,
53697                             fieldLabel: "Email Address",
53698                             name: 'username',
53699                             width:200,
53700                             autoCreate : {tag: "input", type: "text", size: "20"}
53701                         },
53702                         {
53703                             xtype : 'TextField',
53704                             xns : Roo.form,
53705                             fieldLabel: "Password",
53706                             inputType: 'password',
53707                             name: 'password',
53708                             width:200,
53709                             autoCreate : {tag: "input", type: "text", size: "20"},
53710                             listeners : {
53711                                 specialkey : function(e,ev) {
53712                                     if (ev.keyCode == 13) {
53713                                         this.form.dialog.el.mask("Logging in");
53714                                         this.form.doAction('submit', {
53715                                             url: this.form.dialog.url,
53716                                             method: this.form.dialog.method
53717                                         });
53718                                     }
53719                                 }
53720                             }  
53721                         },
53722                         {
53723                             xtype : 'ComboBox',
53724                             xns : Roo.form,
53725                             fieldLabel: "Language",
53726                             name : 'langdisp',
53727                             store: {
53728                                 xtype : 'SimpleStore',
53729                                 fields: ['lang', 'ldisp'],
53730                                 data : [
53731                                     [ 'en', 'English' ],
53732                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53733                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53734                                 ]
53735                             },
53736                             
53737                             valueField : 'lang',
53738                             hiddenName:  'lang',
53739                             width: 200,
53740                             displayField:'ldisp',
53741                             typeAhead: false,
53742                             editable: false,
53743                             mode: 'local',
53744                             triggerAction: 'all',
53745                             emptyText:'Select a Language...',
53746                             selectOnFocus:true,
53747                             listeners : {
53748                                 select :  function(cb, rec, ix) {
53749                                     this.form.switchLang(rec.data.lang);
53750                                 }
53751                             }
53752                         
53753                         }
53754                     ]
53755                 }
53756                   
53757                 
53758             ]
53759         }
53760     ],
53761     buttons : [
53762         {
53763             xtype : 'Button',
53764             xns : 'Roo',
53765             text : "Forgot Password",
53766             listeners : {
53767                 click : function() {
53768                     //console.log(this);
53769                     var n = this.form.findField('username').getValue();
53770                     if (!n.length) {
53771                         Roo.MessageBox.alert("Error", "Fill in your email address");
53772                         return;
53773                     }
53774                     Roo.Ajax.request({
53775                         url: this.dialog.url,
53776                         params: {
53777                             passwordRequest: n
53778                         },
53779                         method: this.dialog.method,
53780                         success:  function(response, opts)  {  // check successfull...
53781                         
53782                             var res = this.dialog.processResponse(response);
53783                             if (!res.success) { // error!
53784                                Roo.MessageBox.alert("Error" ,
53785                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53786                                return;
53787                             }
53788                             Roo.MessageBox.alert("Notice" ,
53789                                 "Please check you email for the Password Reset message");
53790                         },
53791                         failure : function() {
53792                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53793                         }
53794                         
53795                     });
53796                 }
53797             }
53798         },
53799         {
53800             xtype : 'Button',
53801             xns : 'Roo',
53802             text : "Login",
53803             listeners : {
53804                 
53805                 click : function () {
53806                         
53807                     this.dialog.el.mask("Logging in");
53808                     this.form.doAction('submit', {
53809                             url: this.dialog.url,
53810                             method: this.dialog.method
53811                     });
53812                 }
53813             }
53814         }
53815     ]
53816   
53817   
53818 })
53819  
53820
53821
53822