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             
40983             // update styles
40984             if (this.styles) { 
40985                 var st = this.tb.fields.item(0);
40986                 
40987                 st.store.removeAll();
40988                
40989                 
40990                 var cn = sel.className.split(/\s+/);
40991                 
40992                 var avs = [];
40993                 if (this.styles['*']) {
40994                     
40995                     Roo.each(this.styles['*'], function(v) {
40996                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40997                     });
40998                 }
40999                 if (this.styles[tn]) { 
41000                     Roo.each(this.styles[tn], function(v) {
41001                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41002                     });
41003                 }
41004                 
41005                 st.store.loadData(avs);
41006                 st.collapse();
41007                 st.setValue(cn);
41008             }
41009             // flag our selected Node.
41010             this.tb.selectedNode = sel;
41011            
41012            
41013             Roo.menu.MenuMgr.hideAll();
41014
41015         }
41016         
41017         if (!updateFooter) {
41018             return;
41019         }
41020         // update the footer
41021         //
41022         var html = '';
41023         
41024         this.footerEls = ans.reverse();
41025         Roo.each(this.footerEls, function(a,i) {
41026             if (!a) { return; }
41027             html += html.length ? ' &gt; '  :  '';
41028             
41029             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
41030             
41031         });
41032        
41033         // 
41034         var sz = this.footDisp.up('td').getSize();
41035         this.footDisp.dom.style.width = (sz.width -10) + 'px';
41036         this.footDisp.dom.style.marginLeft = '5px';
41037         
41038         this.footDisp.dom.style.overflow = 'hidden';
41039         
41040         this.footDisp.dom.innerHTML = html;
41041             
41042         //this.editorsyncValue();
41043     },
41044    
41045        
41046     // private
41047     onDestroy : function(){
41048         if(this.rendered){
41049             
41050             this.tb.items.each(function(item){
41051                 if(item.menu){
41052                     item.menu.removeAll();
41053                     if(item.menu.el){
41054                         item.menu.el.destroy();
41055                     }
41056                 }
41057                 item.destroy();
41058             });
41059              
41060         }
41061     },
41062     onFirstFocus: function() {
41063         // need to do this for all the toolbars..
41064         this.tb.items.each(function(item){
41065            item.enable();
41066         });
41067     },
41068     buildToolbar: function(tlist, nm)
41069     {
41070         var editor = this.editor;
41071          // create a new element.
41072         var wdiv = editor.wrap.createChild({
41073                 tag: 'div'
41074             }, editor.wrap.dom.firstChild.nextSibling, true);
41075         
41076        
41077         var tb = new Roo.Toolbar(wdiv);
41078         // add the name..
41079         
41080         tb.add(nm+ ":&nbsp;");
41081         
41082         var styles = [];
41083         for(var i in this.styles) {
41084             styles.push(i);
41085         }
41086         
41087         // styles...
41088         if (styles && styles.length) {
41089             
41090             // this needs a multi-select checkbox...
41091             tb.addField( new Roo.form.ComboBox({
41092                 store: new Roo.data.SimpleStore({
41093                     id : 'val',
41094                     fields: ['val', 'selected'],
41095                     data : [] 
41096                 }),
41097                 name : 'className',
41098                 displayField:'val',
41099                 typeAhead: false,
41100                 mode: 'local',
41101                 editable : false,
41102                 triggerAction: 'all',
41103                 emptyText:'Select Style',
41104                 selectOnFocus:true,
41105                 width: 130,
41106                 listeners : {
41107                     'select': function(c, r, i) {
41108                         // initial support only for on class per el..
41109                         tb.selectedNode.className =  r ? r.get('val') : '';
41110                         editor.syncValue();
41111                     }
41112                 }
41113     
41114             }));
41115         }
41116             
41117         
41118         
41119         for (var i in tlist) {
41120             
41121             var item = tlist[i];
41122             tb.add(item.title + ":&nbsp;");
41123             
41124             
41125             
41126             
41127             if (item.opts) {
41128                 // opts == pulldown..
41129                 tb.addField( new Roo.form.ComboBox({
41130                     store: new Roo.data.SimpleStore({
41131                         id : 'val',
41132                         fields: ['val'],
41133                         data : item.opts  
41134                     }),
41135                     name : i,
41136                     displayField:'val',
41137                     typeAhead: false,
41138                     mode: 'local',
41139                     editable : false,
41140                     triggerAction: 'all',
41141                     emptyText:'Select',
41142                     selectOnFocus:true,
41143                     width: item.width ? item.width  : 130,
41144                     listeners : {
41145                         'select': function(c, r, i) {
41146                             tb.selectedNode.setAttribute(c.name, r.get('val'));
41147                         }
41148                     }
41149
41150                 }));
41151                 continue;
41152                     
41153                  
41154                 
41155                 tb.addField( new Roo.form.TextField({
41156                     name: i,
41157                     width: 100,
41158                     //allowBlank:false,
41159                     value: ''
41160                 }));
41161                 continue;
41162             }
41163             tb.addField( new Roo.form.TextField({
41164                 name: i,
41165                 width: item.width,
41166                 //allowBlank:true,
41167                 value: '',
41168                 listeners: {
41169                     'change' : function(f, nv, ov) {
41170                         tb.selectedNode.setAttribute(f.name, nv);
41171                     }
41172                 }
41173             }));
41174              
41175         }
41176         tb.el.on('click', function(e){
41177             e.preventDefault(); // what does this do?
41178         });
41179         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41180         tb.el.hide();
41181         tb.name = nm;
41182         // dont need to disable them... as they will get hidden
41183         return tb;
41184          
41185         
41186     },
41187     buildFooter : function()
41188     {
41189         
41190         var fel = this.editor.wrap.createChild();
41191         this.footer = new Roo.Toolbar(fel);
41192         // toolbar has scrolly on left / right?
41193         var footDisp= new Roo.Toolbar.Fill();
41194         var _t = this;
41195         this.footer.add(
41196             {
41197                 text : '&lt;',
41198                 xtype: 'Button',
41199                 handler : function() {
41200                     _t.footDisp.scrollTo('left',0,true)
41201                 }
41202             }
41203         );
41204         this.footer.add( footDisp );
41205         this.footer.add( 
41206             {
41207                 text : '&gt;',
41208                 xtype: 'Button',
41209                 handler : function() {
41210                     // no animation..
41211                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41212                 }
41213             }
41214         );
41215         var fel = Roo.get(footDisp.el);
41216         fel.addClass('x-editor-context');
41217         this.footDispWrap = fel; 
41218         this.footDispWrap.overflow  = 'hidden';
41219         
41220         this.footDisp = fel.createChild();
41221         this.footDispWrap.on('click', this.onContextClick, this)
41222         
41223         
41224     },
41225     onContextClick : function (ev,dom)
41226     {
41227         ev.preventDefault();
41228         var  cn = dom.className;
41229         Roo.log(cn);
41230         if (!cn.match(/x-ed-loc-/)) {
41231             return;
41232         }
41233         var n = cn.split('-').pop();
41234         var ans = this.footerEls;
41235         var sel = ans[n];
41236         
41237          // pick
41238         var range = this.editor.createRange();
41239         
41240         range.selectNodeContents(sel);
41241         //range.selectNode(sel);
41242         
41243         
41244         var selection = this.editor.getSelection();
41245         selection.removeAllRanges();
41246         selection.addRange(range);
41247         
41248         
41249         
41250         this.updateToolbar(null, null, sel);
41251         
41252         
41253     }
41254     
41255     
41256     
41257     
41258     
41259 });
41260
41261
41262
41263
41264
41265 /*
41266  * Based on:
41267  * Ext JS Library 1.1.1
41268  * Copyright(c) 2006-2007, Ext JS, LLC.
41269  *
41270  * Originally Released Under LGPL - original licence link has changed is not relivant.
41271  *
41272  * Fork - LGPL
41273  * <script type="text/javascript">
41274  */
41275  
41276 /**
41277  * @class Roo.form.BasicForm
41278  * @extends Roo.util.Observable
41279  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41280  * @constructor
41281  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41282  * @param {Object} config Configuration options
41283  */
41284 Roo.form.BasicForm = function(el, config){
41285     this.allItems = [];
41286     this.childForms = [];
41287     Roo.apply(this, config);
41288     /*
41289      * The Roo.form.Field items in this form.
41290      * @type MixedCollection
41291      */
41292      
41293      
41294     this.items = new Roo.util.MixedCollection(false, function(o){
41295         return o.id || (o.id = Roo.id());
41296     });
41297     this.addEvents({
41298         /**
41299          * @event beforeaction
41300          * Fires before any action is performed. Return false to cancel the action.
41301          * @param {Form} this
41302          * @param {Action} action The action to be performed
41303          */
41304         beforeaction: true,
41305         /**
41306          * @event actionfailed
41307          * Fires when an action fails.
41308          * @param {Form} this
41309          * @param {Action} action The action that failed
41310          */
41311         actionfailed : true,
41312         /**
41313          * @event actioncomplete
41314          * Fires when an action is completed.
41315          * @param {Form} this
41316          * @param {Action} action The action that completed
41317          */
41318         actioncomplete : true
41319     });
41320     if(el){
41321         this.initEl(el);
41322     }
41323     Roo.form.BasicForm.superclass.constructor.call(this);
41324 };
41325
41326 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41327     /**
41328      * @cfg {String} method
41329      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41330      */
41331     /**
41332      * @cfg {DataReader} reader
41333      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41334      * This is optional as there is built-in support for processing JSON.
41335      */
41336     /**
41337      * @cfg {DataReader} errorReader
41338      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41339      * This is completely optional as there is built-in support for processing JSON.
41340      */
41341     /**
41342      * @cfg {String} url
41343      * The URL to use for form actions if one isn't supplied in the action options.
41344      */
41345     /**
41346      * @cfg {Boolean} fileUpload
41347      * Set to true if this form is a file upload.
41348      */
41349      
41350     /**
41351      * @cfg {Object} baseParams
41352      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41353      */
41354      /**
41355      
41356     /**
41357      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41358      */
41359     timeout: 30,
41360
41361     // private
41362     activeAction : null,
41363
41364     /**
41365      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41366      * or setValues() data instead of when the form was first created.
41367      */
41368     trackResetOnLoad : false,
41369     
41370     
41371     /**
41372      * childForms - used for multi-tab forms
41373      * @type {Array}
41374      */
41375     childForms : false,
41376     
41377     /**
41378      * allItems - full list of fields.
41379      * @type {Array}
41380      */
41381     allItems : false,
41382     
41383     /**
41384      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41385      * element by passing it or its id or mask the form itself by passing in true.
41386      * @type Mixed
41387      */
41388     waitMsgTarget : false,
41389
41390     // private
41391     initEl : function(el){
41392         this.el = Roo.get(el);
41393         this.id = this.el.id || Roo.id();
41394         this.el.on('submit', this.onSubmit, this);
41395         this.el.addClass('x-form');
41396     },
41397
41398     // private
41399     onSubmit : function(e){
41400         e.stopEvent();
41401     },
41402
41403     /**
41404      * Returns true if client-side validation on the form is successful.
41405      * @return Boolean
41406      */
41407     isValid : function(){
41408         var valid = true;
41409         this.items.each(function(f){
41410            if(!f.validate()){
41411                valid = false;
41412            }
41413         });
41414         return valid;
41415     },
41416
41417     /**
41418      * Returns true if any fields in this form have changed since their original load.
41419      * @return Boolean
41420      */
41421     isDirty : function(){
41422         var dirty = false;
41423         this.items.each(function(f){
41424            if(f.isDirty()){
41425                dirty = true;
41426                return false;
41427            }
41428         });
41429         return dirty;
41430     },
41431
41432     /**
41433      * Performs a predefined action (submit or load) or custom actions you define on this form.
41434      * @param {String} actionName The name of the action type
41435      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41436      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41437      * accept other config options):
41438      * <pre>
41439 Property          Type             Description
41440 ----------------  ---------------  ----------------------------------------------------------------------------------
41441 url               String           The url for the action (defaults to the form's url)
41442 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41443 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41444 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41445                                    validate the form on the client (defaults to false)
41446      * </pre>
41447      * @return {BasicForm} this
41448      */
41449     doAction : function(action, options){
41450         if(typeof action == 'string'){
41451             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41452         }
41453         if(this.fireEvent('beforeaction', this, action) !== false){
41454             this.beforeAction(action);
41455             action.run.defer(100, action);
41456         }
41457         return this;
41458     },
41459
41460     /**
41461      * Shortcut to do a submit action.
41462      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41463      * @return {BasicForm} this
41464      */
41465     submit : function(options){
41466         this.doAction('submit', options);
41467         return this;
41468     },
41469
41470     /**
41471      * Shortcut to do a load action.
41472      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41473      * @return {BasicForm} this
41474      */
41475     load : function(options){
41476         this.doAction('load', options);
41477         return this;
41478     },
41479
41480     /**
41481      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41482      * @param {Record} record The record to edit
41483      * @return {BasicForm} this
41484      */
41485     updateRecord : function(record){
41486         record.beginEdit();
41487         var fs = record.fields;
41488         fs.each(function(f){
41489             var field = this.findField(f.name);
41490             if(field){
41491                 record.set(f.name, field.getValue());
41492             }
41493         }, this);
41494         record.endEdit();
41495         return this;
41496     },
41497
41498     /**
41499      * Loads an Roo.data.Record into this form.
41500      * @param {Record} record The record to load
41501      * @return {BasicForm} this
41502      */
41503     loadRecord : function(record){
41504         this.setValues(record.data);
41505         return this;
41506     },
41507
41508     // private
41509     beforeAction : function(action){
41510         var o = action.options;
41511         
41512        
41513         if(this.waitMsgTarget === true){
41514             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41515         }else if(this.waitMsgTarget){
41516             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41517             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41518         }else {
41519             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41520         }
41521          
41522     },
41523
41524     // private
41525     afterAction : function(action, success){
41526         this.activeAction = null;
41527         var o = action.options;
41528         
41529         if(this.waitMsgTarget === true){
41530             this.el.unmask();
41531         }else if(this.waitMsgTarget){
41532             this.waitMsgTarget.unmask();
41533         }else{
41534             Roo.MessageBox.updateProgress(1);
41535             Roo.MessageBox.hide();
41536         }
41537          
41538         if(success){
41539             if(o.reset){
41540                 this.reset();
41541             }
41542             Roo.callback(o.success, o.scope, [this, action]);
41543             this.fireEvent('actioncomplete', this, action);
41544             
41545         }else{
41546             
41547             // failure condition..
41548             // we have a scenario where updates need confirming.
41549             // eg. if a locking scenario exists..
41550             // we look for { errors : { needs_confirm : true }} in the response.
41551             if (typeof(action.result.errors.needs_confirm) != 'undefined') {
41552                 var _t = this;
41553                 Roo.MessageBox.confirm(
41554                     "Change requires confirmation",
41555                     action.result.errorMsg,
41556                     function(r) {
41557                         if (r != 'yes') {
41558                             return;
41559                         }
41560                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
41561                     }
41562                     
41563                 );
41564                 
41565                 
41566                 
41567                 return;
41568             }
41569             
41570             Roo.callback(o.failure, o.scope, [this, action]);
41571             // show an error message if no failed handler is set..
41572             if (!this.hasListener('actionfailed')) {
41573                 Roo.MessageBox.alert("Error",
41574                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41575                         action.result.errorMsg :
41576                         "Saving Failed, please check your entries"
41577                 );
41578             }
41579             
41580             this.fireEvent('actionfailed', this, action);
41581         }
41582         
41583     },
41584
41585     /**
41586      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41587      * @param {String} id The value to search for
41588      * @return Field
41589      */
41590     findField : function(id){
41591         var field = this.items.get(id);
41592         if(!field){
41593             this.items.each(function(f){
41594                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41595                     field = f;
41596                     return false;
41597                 }
41598             });
41599         }
41600         return field || null;
41601     },
41602
41603     /**
41604      * Add a secondary form to this one, 
41605      * Used to provide tabbed forms. One form is primary, with hidden values 
41606      * which mirror the elements from the other forms.
41607      * 
41608      * @param {Roo.form.Form} form to add.
41609      * 
41610      */
41611     addForm : function(form)
41612     {
41613        
41614         if (this.childForms.indexOf(form) > -1) {
41615             // already added..
41616             return;
41617         }
41618         this.childForms.push(form);
41619         var n = '';
41620         Roo.each(form.allItems, function (fe) {
41621             
41622             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41623             if (this.findField(n)) { // already added..
41624                 return;
41625             }
41626             var add = new Roo.form.Hidden({
41627                 name : n
41628             });
41629             add.render(this.el);
41630             
41631             this.add( add );
41632         }, this);
41633         
41634     },
41635     /**
41636      * Mark fields in this form invalid in bulk.
41637      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41638      * @return {BasicForm} this
41639      */
41640     markInvalid : function(errors){
41641         if(errors instanceof Array){
41642             for(var i = 0, len = errors.length; i < len; i++){
41643                 var fieldError = errors[i];
41644                 var f = this.findField(fieldError.id);
41645                 if(f){
41646                     f.markInvalid(fieldError.msg);
41647                 }
41648             }
41649         }else{
41650             var field, id;
41651             for(id in errors){
41652                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41653                     field.markInvalid(errors[id]);
41654                 }
41655             }
41656         }
41657         Roo.each(this.childForms || [], function (f) {
41658             f.markInvalid(errors);
41659         });
41660         
41661         return this;
41662     },
41663
41664     /**
41665      * Set values for fields in this form in bulk.
41666      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41667      * @return {BasicForm} this
41668      */
41669     setValues : function(values){
41670         if(values instanceof Array){ // array of objects
41671             for(var i = 0, len = values.length; i < len; i++){
41672                 var v = values[i];
41673                 var f = this.findField(v.id);
41674                 if(f){
41675                     f.setValue(v.value);
41676                     if(this.trackResetOnLoad){
41677                         f.originalValue = f.getValue();
41678                     }
41679                 }
41680             }
41681         }else{ // object hash
41682             var field, id;
41683             for(id in values){
41684                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41685                     
41686                     if (field.setFromData && 
41687                         field.valueField && 
41688                         field.displayField &&
41689                         // combos' with local stores can 
41690                         // be queried via setValue()
41691                         // to set their value..
41692                         (field.store && !field.store.isLocal)
41693                         ) {
41694                         // it's a combo
41695                         var sd = { };
41696                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41697                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41698                         field.setFromData(sd);
41699                         
41700                     } else {
41701                         field.setValue(values[id]);
41702                     }
41703                     
41704                     
41705                     if(this.trackResetOnLoad){
41706                         field.originalValue = field.getValue();
41707                     }
41708                 }
41709             }
41710         }
41711          
41712         Roo.each(this.childForms || [], function (f) {
41713             f.setValues(values);
41714         });
41715                 
41716         return this;
41717     },
41718
41719     /**
41720      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41721      * they are returned as an array.
41722      * @param {Boolean} asString
41723      * @return {Object}
41724      */
41725     getValues : function(asString){
41726         if (this.childForms) {
41727             // copy values from the child forms
41728             Roo.each(this.childForms, function (f) {
41729                 this.setValues(f.getValues());
41730             }, this);
41731         }
41732         
41733         
41734         
41735         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41736         if(asString === true){
41737             return fs;
41738         }
41739         return Roo.urlDecode(fs);
41740     },
41741     
41742     /**
41743      * Returns the fields in this form as an object with key/value pairs. 
41744      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41745      * @return {Object}
41746      */
41747     getFieldValues : function(with_hidden)
41748     {
41749         if (this.childForms) {
41750             // copy values from the child forms
41751             // should this call getFieldValues - probably not as we do not currently copy
41752             // hidden fields when we generate..
41753             Roo.each(this.childForms, function (f) {
41754                 this.setValues(f.getValues());
41755             }, this);
41756         }
41757         
41758         var ret = {};
41759         this.items.each(function(f){
41760             if (!f.getName()) {
41761                 return;
41762             }
41763             var v = f.getValue();
41764             // not sure if this supported any more..
41765             if ((typeof(v) == 'object') && f.getRawValue) {
41766                 v = f.getRawValue() ; // dates..
41767             }
41768             // combo boxes where name != hiddenName...
41769             if (f.name != f.getName()) {
41770                 ret[f.name] = f.getRawValue();
41771             }
41772             ret[f.getName()] = v;
41773         });
41774         
41775         return ret;
41776     },
41777
41778     /**
41779      * Clears all invalid messages in this form.
41780      * @return {BasicForm} this
41781      */
41782     clearInvalid : function(){
41783         this.items.each(function(f){
41784            f.clearInvalid();
41785         });
41786         
41787         Roo.each(this.childForms || [], function (f) {
41788             f.clearInvalid();
41789         });
41790         
41791         
41792         return this;
41793     },
41794
41795     /**
41796      * Resets this form.
41797      * @return {BasicForm} this
41798      */
41799     reset : function(){
41800         this.items.each(function(f){
41801             f.reset();
41802         });
41803         
41804         Roo.each(this.childForms || [], function (f) {
41805             f.reset();
41806         });
41807        
41808         
41809         return this;
41810     },
41811
41812     /**
41813      * Add Roo.form components to this form.
41814      * @param {Field} field1
41815      * @param {Field} field2 (optional)
41816      * @param {Field} etc (optional)
41817      * @return {BasicForm} this
41818      */
41819     add : function(){
41820         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41821         return this;
41822     },
41823
41824
41825     /**
41826      * Removes a field from the items collection (does NOT remove its markup).
41827      * @param {Field} field
41828      * @return {BasicForm} this
41829      */
41830     remove : function(field){
41831         this.items.remove(field);
41832         return this;
41833     },
41834
41835     /**
41836      * Looks at the fields in this form, checks them for an id attribute,
41837      * and calls applyTo on the existing dom element with that id.
41838      * @return {BasicForm} this
41839      */
41840     render : function(){
41841         this.items.each(function(f){
41842             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41843                 f.applyTo(f.id);
41844             }
41845         });
41846         return this;
41847     },
41848
41849     /**
41850      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41851      * @param {Object} values
41852      * @return {BasicForm} this
41853      */
41854     applyToFields : function(o){
41855         this.items.each(function(f){
41856            Roo.apply(f, o);
41857         });
41858         return this;
41859     },
41860
41861     /**
41862      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41863      * @param {Object} values
41864      * @return {BasicForm} this
41865      */
41866     applyIfToFields : function(o){
41867         this.items.each(function(f){
41868            Roo.applyIf(f, o);
41869         });
41870         return this;
41871     }
41872 });
41873
41874 // back compat
41875 Roo.BasicForm = Roo.form.BasicForm;/*
41876  * Based on:
41877  * Ext JS Library 1.1.1
41878  * Copyright(c) 2006-2007, Ext JS, LLC.
41879  *
41880  * Originally Released Under LGPL - original licence link has changed is not relivant.
41881  *
41882  * Fork - LGPL
41883  * <script type="text/javascript">
41884  */
41885
41886 /**
41887  * @class Roo.form.Form
41888  * @extends Roo.form.BasicForm
41889  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41890  * @constructor
41891  * @param {Object} config Configuration options
41892  */
41893 Roo.form.Form = function(config){
41894     var xitems =  [];
41895     if (config.items) {
41896         xitems = config.items;
41897         delete config.items;
41898     }
41899    
41900     
41901     Roo.form.Form.superclass.constructor.call(this, null, config);
41902     this.url = this.url || this.action;
41903     if(!this.root){
41904         this.root = new Roo.form.Layout(Roo.applyIf({
41905             id: Roo.id()
41906         }, config));
41907     }
41908     this.active = this.root;
41909     /**
41910      * Array of all the buttons that have been added to this form via {@link addButton}
41911      * @type Array
41912      */
41913     this.buttons = [];
41914     this.allItems = [];
41915     this.addEvents({
41916         /**
41917          * @event clientvalidation
41918          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41919          * @param {Form} this
41920          * @param {Boolean} valid true if the form has passed client-side validation
41921          */
41922         clientvalidation: true,
41923         /**
41924          * @event rendered
41925          * Fires when the form is rendered
41926          * @param {Roo.form.Form} form
41927          */
41928         rendered : true
41929     });
41930     
41931     if (this.progressUrl) {
41932             // push a hidden field onto the list of fields..
41933             this.addxtype( {
41934                     xns: Roo.form, 
41935                     xtype : 'Hidden', 
41936                     name : 'UPLOAD_IDENTIFIER' 
41937             });
41938         }
41939         
41940     
41941     Roo.each(xitems, this.addxtype, this);
41942     
41943     
41944     
41945 };
41946
41947 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41948     /**
41949      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41950      */
41951     /**
41952      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41953      */
41954     /**
41955      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41956      */
41957     buttonAlign:'center',
41958
41959     /**
41960      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41961      */
41962     minButtonWidth:75,
41963
41964     /**
41965      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41966      * This property cascades to child containers if not set.
41967      */
41968     labelAlign:'left',
41969
41970     /**
41971      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41972      * fires a looping event with that state. This is required to bind buttons to the valid
41973      * state using the config value formBind:true on the button.
41974      */
41975     monitorValid : false,
41976
41977     /**
41978      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41979      */
41980     monitorPoll : 200,
41981     
41982     /**
41983      * @cfg {String} progressUrl - Url to return progress data 
41984      */
41985     
41986     progressUrl : false,
41987   
41988     /**
41989      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41990      * fields are added and the column is closed. If no fields are passed the column remains open
41991      * until end() is called.
41992      * @param {Object} config The config to pass to the column
41993      * @param {Field} field1 (optional)
41994      * @param {Field} field2 (optional)
41995      * @param {Field} etc (optional)
41996      * @return Column The column container object
41997      */
41998     column : function(c){
41999         var col = new Roo.form.Column(c);
42000         this.start(col);
42001         if(arguments.length > 1){ // duplicate code required because of Opera
42002             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42003             this.end();
42004         }
42005         return col;
42006     },
42007
42008     /**
42009      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
42010      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
42011      * until end() is called.
42012      * @param {Object} config The config to pass to the fieldset
42013      * @param {Field} field1 (optional)
42014      * @param {Field} field2 (optional)
42015      * @param {Field} etc (optional)
42016      * @return FieldSet The fieldset container object
42017      */
42018     fieldset : function(c){
42019         var fs = new Roo.form.FieldSet(c);
42020         this.start(fs);
42021         if(arguments.length > 1){ // duplicate code required because of Opera
42022             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42023             this.end();
42024         }
42025         return fs;
42026     },
42027
42028     /**
42029      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
42030      * fields are added and the container is closed. If no fields are passed the container remains open
42031      * until end() is called.
42032      * @param {Object} config The config to pass to the Layout
42033      * @param {Field} field1 (optional)
42034      * @param {Field} field2 (optional)
42035      * @param {Field} etc (optional)
42036      * @return Layout The container object
42037      */
42038     container : function(c){
42039         var l = new Roo.form.Layout(c);
42040         this.start(l);
42041         if(arguments.length > 1){ // duplicate code required because of Opera
42042             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42043             this.end();
42044         }
42045         return l;
42046     },
42047
42048     /**
42049      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
42050      * @param {Object} container A Roo.form.Layout or subclass of Layout
42051      * @return {Form} this
42052      */
42053     start : function(c){
42054         // cascade label info
42055         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
42056         this.active.stack.push(c);
42057         c.ownerCt = this.active;
42058         this.active = c;
42059         return this;
42060     },
42061
42062     /**
42063      * Closes the current open container
42064      * @return {Form} this
42065      */
42066     end : function(){
42067         if(this.active == this.root){
42068             return this;
42069         }
42070         this.active = this.active.ownerCt;
42071         return this;
42072     },
42073
42074     /**
42075      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
42076      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
42077      * as the label of the field.
42078      * @param {Field} field1
42079      * @param {Field} field2 (optional)
42080      * @param {Field} etc. (optional)
42081      * @return {Form} this
42082      */
42083     add : function(){
42084         this.active.stack.push.apply(this.active.stack, arguments);
42085         this.allItems.push.apply(this.allItems,arguments);
42086         var r = [];
42087         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
42088             if(a[i].isFormField){
42089                 r.push(a[i]);
42090             }
42091         }
42092         if(r.length > 0){
42093             Roo.form.Form.superclass.add.apply(this, r);
42094         }
42095         return this;
42096     },
42097     
42098
42099     
42100     
42101     
42102      /**
42103      * Find any element that has been added to a form, using it's ID or name
42104      * This can include framesets, columns etc. along with regular fields..
42105      * @param {String} id - id or name to find.
42106      
42107      * @return {Element} e - or false if nothing found.
42108      */
42109     findbyId : function(id)
42110     {
42111         var ret = false;
42112         if (!id) {
42113             return ret;
42114         }
42115         Roo.each(this.allItems, function(f){
42116             if (f.id == id || f.name == id ){
42117                 ret = f;
42118                 return false;
42119             }
42120         });
42121         return ret;
42122     },
42123
42124     
42125     
42126     /**
42127      * Render this form into the passed container. This should only be called once!
42128      * @param {String/HTMLElement/Element} container The element this component should be rendered into
42129      * @return {Form} this
42130      */
42131     render : function(ct)
42132     {
42133         
42134         
42135         
42136         ct = Roo.get(ct);
42137         var o = this.autoCreate || {
42138             tag: 'form',
42139             method : this.method || 'POST',
42140             id : this.id || Roo.id()
42141         };
42142         this.initEl(ct.createChild(o));
42143
42144         this.root.render(this.el);
42145         
42146        
42147              
42148         this.items.each(function(f){
42149             f.render('x-form-el-'+f.id);
42150         });
42151
42152         if(this.buttons.length > 0){
42153             // tables are required to maintain order and for correct IE layout
42154             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
42155                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
42156                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
42157             }}, null, true);
42158             var tr = tb.getElementsByTagName('tr')[0];
42159             for(var i = 0, len = this.buttons.length; i < len; i++) {
42160                 var b = this.buttons[i];
42161                 var td = document.createElement('td');
42162                 td.className = 'x-form-btn-td';
42163                 b.render(tr.appendChild(td));
42164             }
42165         }
42166         if(this.monitorValid){ // initialize after render
42167             this.startMonitoring();
42168         }
42169         this.fireEvent('rendered', this);
42170         return this;
42171     },
42172
42173     /**
42174      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42175      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42176      * object or a valid Roo.DomHelper element config
42177      * @param {Function} handler The function called when the button is clicked
42178      * @param {Object} scope (optional) The scope of the handler function
42179      * @return {Roo.Button}
42180      */
42181     addButton : function(config, handler, scope){
42182         var bc = {
42183             handler: handler,
42184             scope: scope,
42185             minWidth: this.minButtonWidth,
42186             hideParent:true
42187         };
42188         if(typeof config == "string"){
42189             bc.text = config;
42190         }else{
42191             Roo.apply(bc, config);
42192         }
42193         var btn = new Roo.Button(null, bc);
42194         this.buttons.push(btn);
42195         return btn;
42196     },
42197
42198      /**
42199      * Adds a series of form elements (using the xtype property as the factory method.
42200      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42201      * @param {Object} config 
42202      */
42203     
42204     addxtype : function()
42205     {
42206         var ar = Array.prototype.slice.call(arguments, 0);
42207         var ret = false;
42208         for(var i = 0; i < ar.length; i++) {
42209             if (!ar[i]) {
42210                 continue; // skip -- if this happends something invalid got sent, we 
42211                 // should ignore it, as basically that interface element will not show up
42212                 // and that should be pretty obvious!!
42213             }
42214             
42215             if (Roo.form[ar[i].xtype]) {
42216                 ar[i].form = this;
42217                 var fe = Roo.factory(ar[i], Roo.form);
42218                 if (!ret) {
42219                     ret = fe;
42220                 }
42221                 fe.form = this;
42222                 if (fe.store) {
42223                     fe.store.form = this;
42224                 }
42225                 if (fe.isLayout) {  
42226                          
42227                     this.start(fe);
42228                     this.allItems.push(fe);
42229                     if (fe.items && fe.addxtype) {
42230                         fe.addxtype.apply(fe, fe.items);
42231                         delete fe.items;
42232                     }
42233                      this.end();
42234                     continue;
42235                 }
42236                 
42237                 
42238                  
42239                 this.add(fe);
42240               //  console.log('adding ' + ar[i].xtype);
42241             }
42242             if (ar[i].xtype == 'Button') {  
42243                 //console.log('adding button');
42244                 //console.log(ar[i]);
42245                 this.addButton(ar[i]);
42246                 this.allItems.push(fe);
42247                 continue;
42248             }
42249             
42250             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42251                 alert('end is not supported on xtype any more, use items');
42252             //    this.end();
42253             //    //console.log('adding end');
42254             }
42255             
42256         }
42257         return ret;
42258     },
42259     
42260     /**
42261      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42262      * option "monitorValid"
42263      */
42264     startMonitoring : function(){
42265         if(!this.bound){
42266             this.bound = true;
42267             Roo.TaskMgr.start({
42268                 run : this.bindHandler,
42269                 interval : this.monitorPoll || 200,
42270                 scope: this
42271             });
42272         }
42273     },
42274
42275     /**
42276      * Stops monitoring of the valid state of this form
42277      */
42278     stopMonitoring : function(){
42279         this.bound = false;
42280     },
42281
42282     // private
42283     bindHandler : function(){
42284         if(!this.bound){
42285             return false; // stops binding
42286         }
42287         var valid = true;
42288         this.items.each(function(f){
42289             if(!f.isValid(true)){
42290                 valid = false;
42291                 return false;
42292             }
42293         });
42294         for(var i = 0, len = this.buttons.length; i < len; i++){
42295             var btn = this.buttons[i];
42296             if(btn.formBind === true && btn.disabled === valid){
42297                 btn.setDisabled(!valid);
42298             }
42299         }
42300         this.fireEvent('clientvalidation', this, valid);
42301     }
42302     
42303     
42304     
42305     
42306     
42307     
42308     
42309     
42310 });
42311
42312
42313 // back compat
42314 Roo.Form = Roo.form.Form;
42315 /*
42316  * Based on:
42317  * Ext JS Library 1.1.1
42318  * Copyright(c) 2006-2007, Ext JS, LLC.
42319  *
42320  * Originally Released Under LGPL - original licence link has changed is not relivant.
42321  *
42322  * Fork - LGPL
42323  * <script type="text/javascript">
42324  */
42325  
42326  /**
42327  * @class Roo.form.Action
42328  * Internal Class used to handle form actions
42329  * @constructor
42330  * @param {Roo.form.BasicForm} el The form element or its id
42331  * @param {Object} config Configuration options
42332  */
42333  
42334  
42335 // define the action interface
42336 Roo.form.Action = function(form, options){
42337     this.form = form;
42338     this.options = options || {};
42339 };
42340 /**
42341  * Client Validation Failed
42342  * @const 
42343  */
42344 Roo.form.Action.CLIENT_INVALID = 'client';
42345 /**
42346  * Server Validation Failed
42347  * @const 
42348  */
42349  Roo.form.Action.SERVER_INVALID = 'server';
42350  /**
42351  * Connect to Server Failed
42352  * @const 
42353  */
42354 Roo.form.Action.CONNECT_FAILURE = 'connect';
42355 /**
42356  * Reading Data from Server Failed
42357  * @const 
42358  */
42359 Roo.form.Action.LOAD_FAILURE = 'load';
42360
42361 Roo.form.Action.prototype = {
42362     type : 'default',
42363     failureType : undefined,
42364     response : undefined,
42365     result : undefined,
42366
42367     // interface method
42368     run : function(options){
42369
42370     },
42371
42372     // interface method
42373     success : function(response){
42374
42375     },
42376
42377     // interface method
42378     handleResponse : function(response){
42379
42380     },
42381
42382     // default connection failure
42383     failure : function(response){
42384         
42385         this.response = response;
42386         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42387         this.form.afterAction(this, false);
42388     },
42389
42390     processResponse : function(response){
42391         this.response = response;
42392         if(!response.responseText){
42393             return true;
42394         }
42395         this.result = this.handleResponse(response);
42396         return this.result;
42397     },
42398
42399     // utility functions used internally
42400     getUrl : function(appendParams){
42401         var url = this.options.url || this.form.url || this.form.el.dom.action;
42402         if(appendParams){
42403             var p = this.getParams();
42404             if(p){
42405                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42406             }
42407         }
42408         return url;
42409     },
42410
42411     getMethod : function(){
42412         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42413     },
42414
42415     getParams : function(){
42416         var bp = this.form.baseParams;
42417         var p = this.options.params;
42418         if(p){
42419             if(typeof p == "object"){
42420                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42421             }else if(typeof p == 'string' && bp){
42422                 p += '&' + Roo.urlEncode(bp);
42423             }
42424         }else if(bp){
42425             p = Roo.urlEncode(bp);
42426         }
42427         return p;
42428     },
42429
42430     createCallback : function(){
42431         return {
42432             success: this.success,
42433             failure: this.failure,
42434             scope: this,
42435             timeout: (this.form.timeout*1000),
42436             upload: this.form.fileUpload ? this.success : undefined
42437         };
42438     }
42439 };
42440
42441 Roo.form.Action.Submit = function(form, options){
42442     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42443 };
42444
42445 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42446     type : 'submit',
42447
42448     haveProgress : false,
42449     uploadComplete : false,
42450     
42451     // uploadProgress indicator.
42452     uploadProgress : function()
42453     {
42454         if (!this.form.progressUrl) {
42455             return;
42456         }
42457         
42458         if (!this.haveProgress) {
42459             Roo.MessageBox.progress("Uploading", "Uploading");
42460         }
42461         if (this.uploadComplete) {
42462            Roo.MessageBox.hide();
42463            return;
42464         }
42465         
42466         this.haveProgress = true;
42467    
42468         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42469         
42470         var c = new Roo.data.Connection();
42471         c.request({
42472             url : this.form.progressUrl,
42473             params: {
42474                 id : uid
42475             },
42476             method: 'GET',
42477             success : function(req){
42478                //console.log(data);
42479                 var rdata = false;
42480                 var edata;
42481                 try  {
42482                    rdata = Roo.decode(req.responseText)
42483                 } catch (e) {
42484                     Roo.log("Invalid data from server..");
42485                     Roo.log(edata);
42486                     return;
42487                 }
42488                 if (!rdata || !rdata.success) {
42489                     Roo.log(rdata);
42490                     return;
42491                 }
42492                 var data = rdata.data;
42493                 
42494                 if (this.uploadComplete) {
42495                    Roo.MessageBox.hide();
42496                    return;
42497                 }
42498                    
42499                 if (data){
42500                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42501                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42502                     );
42503                 }
42504                 this.uploadProgress.defer(2000,this);
42505             },
42506        
42507             failure: function(data) {
42508                 Roo.log('progress url failed ');
42509                 Roo.log(data);
42510             },
42511             scope : this
42512         });
42513            
42514     },
42515     
42516     
42517     run : function()
42518     {
42519         // run get Values on the form, so it syncs any secondary forms.
42520         this.form.getValues();
42521         
42522         var o = this.options;
42523         var method = this.getMethod();
42524         var isPost = method == 'POST';
42525         if(o.clientValidation === false || this.form.isValid()){
42526             
42527             if (this.form.progressUrl) {
42528                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42529                     (new Date() * 1) + '' + Math.random());
42530                     
42531             } 
42532             
42533             
42534             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42535                 form:this.form.el.dom,
42536                 url:this.getUrl(!isPost),
42537                 method: method,
42538                 params:isPost ? this.getParams() : null,
42539                 isUpload: this.form.fileUpload
42540             }));
42541             
42542             this.uploadProgress();
42543
42544         }else if (o.clientValidation !== false){ // client validation failed
42545             this.failureType = Roo.form.Action.CLIENT_INVALID;
42546             this.form.afterAction(this, false);
42547         }
42548     },
42549
42550     success : function(response)
42551     {
42552         this.uploadComplete= true;
42553         if (this.haveProgress) {
42554             Roo.MessageBox.hide();
42555         }
42556         
42557         
42558         var result = this.processResponse(response);
42559         if(result === true || result.success){
42560             this.form.afterAction(this, true);
42561             return;
42562         }
42563         if(result.errors){
42564             this.form.markInvalid(result.errors);
42565             this.failureType = Roo.form.Action.SERVER_INVALID;
42566         }
42567         this.form.afterAction(this, false);
42568     },
42569     failure : function(response)
42570     {
42571         this.uploadComplete= true;
42572         if (this.haveProgress) {
42573             Roo.MessageBox.hide();
42574         }
42575         
42576         this.response = response;
42577         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42578         this.form.afterAction(this, false);
42579     },
42580     
42581     handleResponse : function(response){
42582         if(this.form.errorReader){
42583             var rs = this.form.errorReader.read(response);
42584             var errors = [];
42585             if(rs.records){
42586                 for(var i = 0, len = rs.records.length; i < len; i++) {
42587                     var r = rs.records[i];
42588                     errors[i] = r.data;
42589                 }
42590             }
42591             if(errors.length < 1){
42592                 errors = null;
42593             }
42594             return {
42595                 success : rs.success,
42596                 errors : errors
42597             };
42598         }
42599         var ret = false;
42600         try {
42601             ret = Roo.decode(response.responseText);
42602         } catch (e) {
42603             ret = {
42604                 success: false,
42605                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42606                 errors : []
42607             };
42608         }
42609         return ret;
42610         
42611     }
42612 });
42613
42614
42615 Roo.form.Action.Load = function(form, options){
42616     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42617     this.reader = this.form.reader;
42618 };
42619
42620 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42621     type : 'load',
42622
42623     run : function(){
42624         
42625         Roo.Ajax.request(Roo.apply(
42626                 this.createCallback(), {
42627                     method:this.getMethod(),
42628                     url:this.getUrl(false),
42629                     params:this.getParams()
42630         }));
42631     },
42632
42633     success : function(response){
42634         
42635         var result = this.processResponse(response);
42636         if(result === true || !result.success || !result.data){
42637             this.failureType = Roo.form.Action.LOAD_FAILURE;
42638             this.form.afterAction(this, false);
42639             return;
42640         }
42641         this.form.clearInvalid();
42642         this.form.setValues(result.data);
42643         this.form.afterAction(this, true);
42644     },
42645
42646     handleResponse : function(response){
42647         if(this.form.reader){
42648             var rs = this.form.reader.read(response);
42649             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42650             return {
42651                 success : rs.success,
42652                 data : data
42653             };
42654         }
42655         return Roo.decode(response.responseText);
42656     }
42657 });
42658
42659 Roo.form.Action.ACTION_TYPES = {
42660     'load' : Roo.form.Action.Load,
42661     'submit' : Roo.form.Action.Submit
42662 };/*
42663  * Based on:
42664  * Ext JS Library 1.1.1
42665  * Copyright(c) 2006-2007, Ext JS, LLC.
42666  *
42667  * Originally Released Under LGPL - original licence link has changed is not relivant.
42668  *
42669  * Fork - LGPL
42670  * <script type="text/javascript">
42671  */
42672  
42673 /**
42674  * @class Roo.form.Layout
42675  * @extends Roo.Component
42676  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42677  * @constructor
42678  * @param {Object} config Configuration options
42679  */
42680 Roo.form.Layout = function(config){
42681     var xitems = [];
42682     if (config.items) {
42683         xitems = config.items;
42684         delete config.items;
42685     }
42686     Roo.form.Layout.superclass.constructor.call(this, config);
42687     this.stack = [];
42688     Roo.each(xitems, this.addxtype, this);
42689      
42690 };
42691
42692 Roo.extend(Roo.form.Layout, Roo.Component, {
42693     /**
42694      * @cfg {String/Object} autoCreate
42695      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42696      */
42697     /**
42698      * @cfg {String/Object/Function} style
42699      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42700      * a function which returns such a specification.
42701      */
42702     /**
42703      * @cfg {String} labelAlign
42704      * Valid values are "left," "top" and "right" (defaults to "left")
42705      */
42706     /**
42707      * @cfg {Number} labelWidth
42708      * Fixed width in pixels of all field labels (defaults to undefined)
42709      */
42710     /**
42711      * @cfg {Boolean} clear
42712      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42713      */
42714     clear : true,
42715     /**
42716      * @cfg {String} labelSeparator
42717      * The separator to use after field labels (defaults to ':')
42718      */
42719     labelSeparator : ':',
42720     /**
42721      * @cfg {Boolean} hideLabels
42722      * True to suppress the display of field labels in this layout (defaults to false)
42723      */
42724     hideLabels : false,
42725
42726     // private
42727     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42728     
42729     isLayout : true,
42730     
42731     // private
42732     onRender : function(ct, position){
42733         if(this.el){ // from markup
42734             this.el = Roo.get(this.el);
42735         }else {  // generate
42736             var cfg = this.getAutoCreate();
42737             this.el = ct.createChild(cfg, position);
42738         }
42739         if(this.style){
42740             this.el.applyStyles(this.style);
42741         }
42742         if(this.labelAlign){
42743             this.el.addClass('x-form-label-'+this.labelAlign);
42744         }
42745         if(this.hideLabels){
42746             this.labelStyle = "display:none";
42747             this.elementStyle = "padding-left:0;";
42748         }else{
42749             if(typeof this.labelWidth == 'number'){
42750                 this.labelStyle = "width:"+this.labelWidth+"px;";
42751                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42752             }
42753             if(this.labelAlign == 'top'){
42754                 this.labelStyle = "width:auto;";
42755                 this.elementStyle = "padding-left:0;";
42756             }
42757         }
42758         var stack = this.stack;
42759         var slen = stack.length;
42760         if(slen > 0){
42761             if(!this.fieldTpl){
42762                 var t = new Roo.Template(
42763                     '<div class="x-form-item {5}">',
42764                         '<label for="{0}" style="{2}">{1}{4}</label>',
42765                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42766                         '</div>',
42767                     '</div><div class="x-form-clear-left"></div>'
42768                 );
42769                 t.disableFormats = true;
42770                 t.compile();
42771                 Roo.form.Layout.prototype.fieldTpl = t;
42772             }
42773             for(var i = 0; i < slen; i++) {
42774                 if(stack[i].isFormField){
42775                     this.renderField(stack[i]);
42776                 }else{
42777                     this.renderComponent(stack[i]);
42778                 }
42779             }
42780         }
42781         if(this.clear){
42782             this.el.createChild({cls:'x-form-clear'});
42783         }
42784     },
42785
42786     // private
42787     renderField : function(f){
42788         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42789                f.id, //0
42790                f.fieldLabel, //1
42791                f.labelStyle||this.labelStyle||'', //2
42792                this.elementStyle||'', //3
42793                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42794                f.itemCls||this.itemCls||''  //5
42795        ], true).getPrevSibling());
42796     },
42797
42798     // private
42799     renderComponent : function(c){
42800         c.render(c.isLayout ? this.el : this.el.createChild());    
42801     },
42802     /**
42803      * Adds a object form elements (using the xtype property as the factory method.)
42804      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42805      * @param {Object} config 
42806      */
42807     addxtype : function(o)
42808     {
42809         // create the lement.
42810         o.form = this.form;
42811         var fe = Roo.factory(o, Roo.form);
42812         this.form.allItems.push(fe);
42813         this.stack.push(fe);
42814         
42815         if (fe.isFormField) {
42816             this.form.items.add(fe);
42817         }
42818          
42819         return fe;
42820     }
42821 });
42822
42823 /**
42824  * @class Roo.form.Column
42825  * @extends Roo.form.Layout
42826  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42827  * @constructor
42828  * @param {Object} config Configuration options
42829  */
42830 Roo.form.Column = function(config){
42831     Roo.form.Column.superclass.constructor.call(this, config);
42832 };
42833
42834 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42835     /**
42836      * @cfg {Number/String} width
42837      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42838      */
42839     /**
42840      * @cfg {String/Object} autoCreate
42841      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42842      */
42843
42844     // private
42845     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42846
42847     // private
42848     onRender : function(ct, position){
42849         Roo.form.Column.superclass.onRender.call(this, ct, position);
42850         if(this.width){
42851             this.el.setWidth(this.width);
42852         }
42853     }
42854 });
42855
42856
42857 /**
42858  * @class Roo.form.Row
42859  * @extends Roo.form.Layout
42860  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42861  * @constructor
42862  * @param {Object} config Configuration options
42863  */
42864
42865  
42866 Roo.form.Row = function(config){
42867     Roo.form.Row.superclass.constructor.call(this, config);
42868 };
42869  
42870 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42871       /**
42872      * @cfg {Number/String} width
42873      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42874      */
42875     /**
42876      * @cfg {Number/String} height
42877      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42878      */
42879     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42880     
42881     padWidth : 20,
42882     // private
42883     onRender : function(ct, position){
42884         //console.log('row render');
42885         if(!this.rowTpl){
42886             var t = new Roo.Template(
42887                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42888                     '<label for="{0}" style="{2}">{1}{4}</label>',
42889                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42890                     '</div>',
42891                 '</div>'
42892             );
42893             t.disableFormats = true;
42894             t.compile();
42895             Roo.form.Layout.prototype.rowTpl = t;
42896         }
42897         this.fieldTpl = this.rowTpl;
42898         
42899         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42900         var labelWidth = 100;
42901         
42902         if ((this.labelAlign != 'top')) {
42903             if (typeof this.labelWidth == 'number') {
42904                 labelWidth = this.labelWidth
42905             }
42906             this.padWidth =  20 + labelWidth;
42907             
42908         }
42909         
42910         Roo.form.Column.superclass.onRender.call(this, ct, position);
42911         if(this.width){
42912             this.el.setWidth(this.width);
42913         }
42914         if(this.height){
42915             this.el.setHeight(this.height);
42916         }
42917     },
42918     
42919     // private
42920     renderField : function(f){
42921         f.fieldEl = this.fieldTpl.append(this.el, [
42922                f.id, f.fieldLabel,
42923                f.labelStyle||this.labelStyle||'',
42924                this.elementStyle||'',
42925                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42926                f.itemCls||this.itemCls||'',
42927                f.width ? f.width + this.padWidth : 160 + this.padWidth
42928        ],true);
42929     }
42930 });
42931  
42932
42933 /**
42934  * @class Roo.form.FieldSet
42935  * @extends Roo.form.Layout
42936  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42937  * @constructor
42938  * @param {Object} config Configuration options
42939  */
42940 Roo.form.FieldSet = function(config){
42941     Roo.form.FieldSet.superclass.constructor.call(this, config);
42942 };
42943
42944 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42945     /**
42946      * @cfg {String} legend
42947      * The text to display as the legend for the FieldSet (defaults to '')
42948      */
42949     /**
42950      * @cfg {String/Object} autoCreate
42951      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42952      */
42953
42954     // private
42955     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42956
42957     // private
42958     onRender : function(ct, position){
42959         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42960         if(this.legend){
42961             this.setLegend(this.legend);
42962         }
42963     },
42964
42965     // private
42966     setLegend : function(text){
42967         if(this.rendered){
42968             this.el.child('legend').update(text);
42969         }
42970     }
42971 });/*
42972  * Based on:
42973  * Ext JS Library 1.1.1
42974  * Copyright(c) 2006-2007, Ext JS, LLC.
42975  *
42976  * Originally Released Under LGPL - original licence link has changed is not relivant.
42977  *
42978  * Fork - LGPL
42979  * <script type="text/javascript">
42980  */
42981 /**
42982  * @class Roo.form.VTypes
42983  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42984  * @singleton
42985  */
42986 Roo.form.VTypes = function(){
42987     // closure these in so they are only created once.
42988     var alpha = /^[a-zA-Z_]+$/;
42989     var alphanum = /^[a-zA-Z0-9_]+$/;
42990     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42991     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42992
42993     // All these messages and functions are configurable
42994     return {
42995         /**
42996          * The function used to validate email addresses
42997          * @param {String} value The email address
42998          */
42999         'email' : function(v){
43000             return email.test(v);
43001         },
43002         /**
43003          * The error text to display when the email validation function returns false
43004          * @type String
43005          */
43006         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
43007         /**
43008          * The keystroke filter mask to be applied on email input
43009          * @type RegExp
43010          */
43011         'emailMask' : /[a-z0-9_\.\-@]/i,
43012
43013         /**
43014          * The function used to validate URLs
43015          * @param {String} value The URL
43016          */
43017         'url' : function(v){
43018             return url.test(v);
43019         },
43020         /**
43021          * The error text to display when the url validation function returns false
43022          * @type String
43023          */
43024         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
43025         
43026         /**
43027          * The function used to validate alpha values
43028          * @param {String} value The value
43029          */
43030         'alpha' : function(v){
43031             return alpha.test(v);
43032         },
43033         /**
43034          * The error text to display when the alpha validation function returns false
43035          * @type String
43036          */
43037         'alphaText' : 'This field should only contain letters and _',
43038         /**
43039          * The keystroke filter mask to be applied on alpha input
43040          * @type RegExp
43041          */
43042         'alphaMask' : /[a-z_]/i,
43043
43044         /**
43045          * The function used to validate alphanumeric values
43046          * @param {String} value The value
43047          */
43048         'alphanum' : function(v){
43049             return alphanum.test(v);
43050         },
43051         /**
43052          * The error text to display when the alphanumeric validation function returns false
43053          * @type String
43054          */
43055         'alphanumText' : 'This field should only contain letters, numbers and _',
43056         /**
43057          * The keystroke filter mask to be applied on alphanumeric input
43058          * @type RegExp
43059          */
43060         'alphanumMask' : /[a-z0-9_]/i
43061     };
43062 }();//<script type="text/javascript">
43063
43064 /**
43065  * @class Roo.form.FCKeditor
43066  * @extends Roo.form.TextArea
43067  * Wrapper around the FCKEditor http://www.fckeditor.net
43068  * @constructor
43069  * Creates a new FCKeditor
43070  * @param {Object} config Configuration options
43071  */
43072 Roo.form.FCKeditor = function(config){
43073     Roo.form.FCKeditor.superclass.constructor.call(this, config);
43074     this.addEvents({
43075          /**
43076          * @event editorinit
43077          * Fired when the editor is initialized - you can add extra handlers here..
43078          * @param {FCKeditor} this
43079          * @param {Object} the FCK object.
43080          */
43081         editorinit : true
43082     });
43083     
43084     
43085 };
43086 Roo.form.FCKeditor.editors = { };
43087 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
43088 {
43089     //defaultAutoCreate : {
43090     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
43091     //},
43092     // private
43093     /**
43094      * @cfg {Object} fck options - see fck manual for details.
43095      */
43096     fckconfig : false,
43097     
43098     /**
43099      * @cfg {Object} fck toolbar set (Basic or Default)
43100      */
43101     toolbarSet : 'Basic',
43102     /**
43103      * @cfg {Object} fck BasePath
43104      */ 
43105     basePath : '/fckeditor/',
43106     
43107     
43108     frame : false,
43109     
43110     value : '',
43111     
43112    
43113     onRender : function(ct, position)
43114     {
43115         if(!this.el){
43116             this.defaultAutoCreate = {
43117                 tag: "textarea",
43118                 style:"width:300px;height:60px;",
43119                 autocomplete: "off"
43120             };
43121         }
43122         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
43123         /*
43124         if(this.grow){
43125             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
43126             if(this.preventScrollbars){
43127                 this.el.setStyle("overflow", "hidden");
43128             }
43129             this.el.setHeight(this.growMin);
43130         }
43131         */
43132         //console.log('onrender' + this.getId() );
43133         Roo.form.FCKeditor.editors[this.getId()] = this;
43134          
43135
43136         this.replaceTextarea() ;
43137         
43138     },
43139     
43140     getEditor : function() {
43141         return this.fckEditor;
43142     },
43143     /**
43144      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43145      * @param {Mixed} value The value to set
43146      */
43147     
43148     
43149     setValue : function(value)
43150     {
43151         //console.log('setValue: ' + value);
43152         
43153         if(typeof(value) == 'undefined') { // not sure why this is happending...
43154             return;
43155         }
43156         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43157         
43158         //if(!this.el || !this.getEditor()) {
43159         //    this.value = value;
43160             //this.setValue.defer(100,this,[value]);    
43161         //    return;
43162         //} 
43163         
43164         if(!this.getEditor()) {
43165             return;
43166         }
43167         
43168         this.getEditor().SetData(value);
43169         
43170         //
43171
43172     },
43173
43174     /**
43175      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43176      * @return {Mixed} value The field value
43177      */
43178     getValue : function()
43179     {
43180         
43181         if (this.frame && this.frame.dom.style.display == 'none') {
43182             return Roo.form.FCKeditor.superclass.getValue.call(this);
43183         }
43184         
43185         if(!this.el || !this.getEditor()) {
43186            
43187            // this.getValue.defer(100,this); 
43188             return this.value;
43189         }
43190        
43191         
43192         var value=this.getEditor().GetData();
43193         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43194         return Roo.form.FCKeditor.superclass.getValue.call(this);
43195         
43196
43197     },
43198
43199     /**
43200      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43201      * @return {Mixed} value The field value
43202      */
43203     getRawValue : function()
43204     {
43205         if (this.frame && this.frame.dom.style.display == 'none') {
43206             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43207         }
43208         
43209         if(!this.el || !this.getEditor()) {
43210             //this.getRawValue.defer(100,this); 
43211             return this.value;
43212             return;
43213         }
43214         
43215         
43216         
43217         var value=this.getEditor().GetData();
43218         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43219         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43220          
43221     },
43222     
43223     setSize : function(w,h) {
43224         
43225         
43226         
43227         //if (this.frame && this.frame.dom.style.display == 'none') {
43228         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43229         //    return;
43230         //}
43231         //if(!this.el || !this.getEditor()) {
43232         //    this.setSize.defer(100,this, [w,h]); 
43233         //    return;
43234         //}
43235         
43236         
43237         
43238         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43239         
43240         this.frame.dom.setAttribute('width', w);
43241         this.frame.dom.setAttribute('height', h);
43242         this.frame.setSize(w,h);
43243         
43244     },
43245     
43246     toggleSourceEdit : function(value) {
43247         
43248       
43249          
43250         this.el.dom.style.display = value ? '' : 'none';
43251         this.frame.dom.style.display = value ?  'none' : '';
43252         
43253     },
43254     
43255     
43256     focus: function(tag)
43257     {
43258         if (this.frame.dom.style.display == 'none') {
43259             return Roo.form.FCKeditor.superclass.focus.call(this);
43260         }
43261         if(!this.el || !this.getEditor()) {
43262             this.focus.defer(100,this, [tag]); 
43263             return;
43264         }
43265         
43266         
43267         
43268         
43269         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43270         this.getEditor().Focus();
43271         if (tgs.length) {
43272             if (!this.getEditor().Selection.GetSelection()) {
43273                 this.focus.defer(100,this, [tag]); 
43274                 return;
43275             }
43276             
43277             
43278             var r = this.getEditor().EditorDocument.createRange();
43279             r.setStart(tgs[0],0);
43280             r.setEnd(tgs[0],0);
43281             this.getEditor().Selection.GetSelection().removeAllRanges();
43282             this.getEditor().Selection.GetSelection().addRange(r);
43283             this.getEditor().Focus();
43284         }
43285         
43286     },
43287     
43288     
43289     
43290     replaceTextarea : function()
43291     {
43292         if ( document.getElementById( this.getId() + '___Frame' ) )
43293             return ;
43294         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43295         //{
43296             // We must check the elements firstly using the Id and then the name.
43297         var oTextarea = document.getElementById( this.getId() );
43298         
43299         var colElementsByName = document.getElementsByName( this.getId() ) ;
43300          
43301         oTextarea.style.display = 'none' ;
43302
43303         if ( oTextarea.tabIndex ) {            
43304             this.TabIndex = oTextarea.tabIndex ;
43305         }
43306         
43307         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43308         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43309         this.frame = Roo.get(this.getId() + '___Frame')
43310     },
43311     
43312     _getConfigHtml : function()
43313     {
43314         var sConfig = '' ;
43315
43316         for ( var o in this.fckconfig ) {
43317             sConfig += sConfig.length > 0  ? '&amp;' : '';
43318             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43319         }
43320
43321         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43322     },
43323     
43324     
43325     _getIFrameHtml : function()
43326     {
43327         var sFile = 'fckeditor.html' ;
43328         /* no idea what this is about..
43329         try
43330         {
43331             if ( (/fcksource=true/i).test( window.top.location.search ) )
43332                 sFile = 'fckeditor.original.html' ;
43333         }
43334         catch (e) { 
43335         */
43336
43337         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43338         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43339         
43340         
43341         var html = '<iframe id="' + this.getId() +
43342             '___Frame" src="' + sLink +
43343             '" width="' + this.width +
43344             '" height="' + this.height + '"' +
43345             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43346             ' frameborder="0" scrolling="no"></iframe>' ;
43347
43348         return html ;
43349     },
43350     
43351     _insertHtmlBefore : function( html, element )
43352     {
43353         if ( element.insertAdjacentHTML )       {
43354             // IE
43355             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43356         } else { // Gecko
43357             var oRange = document.createRange() ;
43358             oRange.setStartBefore( element ) ;
43359             var oFragment = oRange.createContextualFragment( html );
43360             element.parentNode.insertBefore( oFragment, element ) ;
43361         }
43362     }
43363     
43364     
43365   
43366     
43367     
43368     
43369     
43370
43371 });
43372
43373 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43374
43375 function FCKeditor_OnComplete(editorInstance){
43376     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43377     f.fckEditor = editorInstance;
43378     //console.log("loaded");
43379     f.fireEvent('editorinit', f, editorInstance);
43380
43381   
43382
43383  
43384
43385
43386
43387
43388
43389
43390
43391
43392
43393
43394
43395
43396
43397
43398
43399 //<script type="text/javascript">
43400 /**
43401  * @class Roo.form.GridField
43402  * @extends Roo.form.Field
43403  * Embed a grid (or editable grid into a form)
43404  * STATUS ALPHA
43405  * 
43406  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43407  * it needs 
43408  * xgrid.store = Roo.data.Store
43409  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43410  * xgrid.store.reader = Roo.data.JsonReader 
43411  * 
43412  * 
43413  * @constructor
43414  * Creates a new GridField
43415  * @param {Object} config Configuration options
43416  */
43417 Roo.form.GridField = function(config){
43418     Roo.form.GridField.superclass.constructor.call(this, config);
43419      
43420 };
43421
43422 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43423     /**
43424      * @cfg {Number} width  - used to restrict width of grid..
43425      */
43426     width : 100,
43427     /**
43428      * @cfg {Number} height - used to restrict height of grid..
43429      */
43430     height : 50,
43431      /**
43432      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43433          * 
43434          *}
43435      */
43436     xgrid : false, 
43437     /**
43438      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43439      * {tag: "input", type: "checkbox", autocomplete: "off"})
43440      */
43441    // defaultAutoCreate : { tag: 'div' },
43442     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43443     /**
43444      * @cfg {String} addTitle Text to include for adding a title.
43445      */
43446     addTitle : false,
43447     //
43448     onResize : function(){
43449         Roo.form.Field.superclass.onResize.apply(this, arguments);
43450     },
43451
43452     initEvents : function(){
43453         // Roo.form.Checkbox.superclass.initEvents.call(this);
43454         // has no events...
43455        
43456     },
43457
43458
43459     getResizeEl : function(){
43460         return this.wrap;
43461     },
43462
43463     getPositionEl : function(){
43464         return this.wrap;
43465     },
43466
43467     // private
43468     onRender : function(ct, position){
43469         
43470         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43471         var style = this.style;
43472         delete this.style;
43473         
43474         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43475         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43476         this.viewEl = this.wrap.createChild({ tag: 'div' });
43477         if (style) {
43478             this.viewEl.applyStyles(style);
43479         }
43480         if (this.width) {
43481             this.viewEl.setWidth(this.width);
43482         }
43483         if (this.height) {
43484             this.viewEl.setHeight(this.height);
43485         }
43486         //if(this.inputValue !== undefined){
43487         //this.setValue(this.value);
43488         
43489         
43490         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43491         
43492         
43493         this.grid.render();
43494         this.grid.getDataSource().on('remove', this.refreshValue, this);
43495         this.grid.getDataSource().on('update', this.refreshValue, this);
43496         this.grid.on('afteredit', this.refreshValue, this);
43497  
43498     },
43499      
43500     
43501     /**
43502      * Sets the value of the item. 
43503      * @param {String} either an object  or a string..
43504      */
43505     setValue : function(v){
43506         //this.value = v;
43507         v = v || []; // empty set..
43508         // this does not seem smart - it really only affects memoryproxy grids..
43509         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43510             var ds = this.grid.getDataSource();
43511             // assumes a json reader..
43512             var data = {}
43513             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43514             ds.loadData( data);
43515         }
43516         // clear selection so it does not get stale.
43517         if (this.grid.sm) { 
43518             this.grid.sm.clearSelections();
43519         }
43520         
43521         Roo.form.GridField.superclass.setValue.call(this, v);
43522         this.refreshValue();
43523         // should load data in the grid really....
43524     },
43525     
43526     // private
43527     refreshValue: function() {
43528          var val = [];
43529         this.grid.getDataSource().each(function(r) {
43530             val.push(r.data);
43531         });
43532         this.el.dom.value = Roo.encode(val);
43533     }
43534     
43535      
43536     
43537     
43538 });/*
43539  * Based on:
43540  * Ext JS Library 1.1.1
43541  * Copyright(c) 2006-2007, Ext JS, LLC.
43542  *
43543  * Originally Released Under LGPL - original licence link has changed is not relivant.
43544  *
43545  * Fork - LGPL
43546  * <script type="text/javascript">
43547  */
43548 /**
43549  * @class Roo.form.DisplayField
43550  * @extends Roo.form.Field
43551  * A generic Field to display non-editable data.
43552  * @constructor
43553  * Creates a new Display Field item.
43554  * @param {Object} config Configuration options
43555  */
43556 Roo.form.DisplayField = function(config){
43557     Roo.form.DisplayField.superclass.constructor.call(this, config);
43558     
43559 };
43560
43561 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43562     inputType:      'hidden',
43563     allowBlank:     true,
43564     readOnly:         true,
43565     
43566  
43567     /**
43568      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43569      */
43570     focusClass : undefined,
43571     /**
43572      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43573      */
43574     fieldClass: 'x-form-field',
43575     
43576      /**
43577      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43578      */
43579     valueRenderer: undefined,
43580     
43581     width: 100,
43582     /**
43583      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43584      * {tag: "input", type: "checkbox", autocomplete: "off"})
43585      */
43586      
43587  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43588
43589     onResize : function(){
43590         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43591         
43592     },
43593
43594     initEvents : function(){
43595         // Roo.form.Checkbox.superclass.initEvents.call(this);
43596         // has no events...
43597        
43598     },
43599
43600
43601     getResizeEl : function(){
43602         return this.wrap;
43603     },
43604
43605     getPositionEl : function(){
43606         return this.wrap;
43607     },
43608
43609     // private
43610     onRender : function(ct, position){
43611         
43612         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43613         //if(this.inputValue !== undefined){
43614         this.wrap = this.el.wrap();
43615         
43616         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43617         
43618         if (this.bodyStyle) {
43619             this.viewEl.applyStyles(this.bodyStyle);
43620         }
43621         //this.viewEl.setStyle('padding', '2px');
43622         
43623         this.setValue(this.value);
43624         
43625     },
43626 /*
43627     // private
43628     initValue : Roo.emptyFn,
43629
43630   */
43631
43632         // private
43633     onClick : function(){
43634         
43635     },
43636
43637     /**
43638      * Sets the checked state of the checkbox.
43639      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43640      */
43641     setValue : function(v){
43642         this.value = v;
43643         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43644         // this might be called before we have a dom element..
43645         if (!this.viewEl) {
43646             return;
43647         }
43648         this.viewEl.dom.innerHTML = html;
43649         Roo.form.DisplayField.superclass.setValue.call(this, v);
43650
43651     }
43652 });/*
43653  * 
43654  * Licence- LGPL
43655  * 
43656  */
43657
43658 /**
43659  * @class Roo.form.DayPicker
43660  * @extends Roo.form.Field
43661  * A Day picker show [M] [T] [W] ....
43662  * @constructor
43663  * Creates a new Day Picker
43664  * @param {Object} config Configuration options
43665  */
43666 Roo.form.DayPicker= function(config){
43667     Roo.form.DayPicker.superclass.constructor.call(this, config);
43668      
43669 };
43670
43671 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43672     /**
43673      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43674      */
43675     focusClass : undefined,
43676     /**
43677      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43678      */
43679     fieldClass: "x-form-field",
43680    
43681     /**
43682      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43683      * {tag: "input", type: "checkbox", autocomplete: "off"})
43684      */
43685     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43686     
43687    
43688     actionMode : 'viewEl', 
43689     //
43690     // private
43691  
43692     inputType : 'hidden',
43693     
43694      
43695     inputElement: false, // real input element?
43696     basedOn: false, // ????
43697     
43698     isFormField: true, // not sure where this is needed!!!!
43699
43700     onResize : function(){
43701         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43702         if(!this.boxLabel){
43703             this.el.alignTo(this.wrap, 'c-c');
43704         }
43705     },
43706
43707     initEvents : function(){
43708         Roo.form.Checkbox.superclass.initEvents.call(this);
43709         this.el.on("click", this.onClick,  this);
43710         this.el.on("change", this.onClick,  this);
43711     },
43712
43713
43714     getResizeEl : function(){
43715         return this.wrap;
43716     },
43717
43718     getPositionEl : function(){
43719         return this.wrap;
43720     },
43721
43722     
43723     // private
43724     onRender : function(ct, position){
43725         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43726        
43727         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43728         
43729         var r1 = '<table><tr>';
43730         var r2 = '<tr class="x-form-daypick-icons">';
43731         for (var i=0; i < 7; i++) {
43732             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43733             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43734         }
43735         
43736         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43737         viewEl.select('img').on('click', this.onClick, this);
43738         this.viewEl = viewEl;   
43739         
43740         
43741         // this will not work on Chrome!!!
43742         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43743         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43744         
43745         
43746           
43747
43748     },
43749
43750     // private
43751     initValue : Roo.emptyFn,
43752
43753     /**
43754      * Returns the checked state of the checkbox.
43755      * @return {Boolean} True if checked, else false
43756      */
43757     getValue : function(){
43758         return this.el.dom.value;
43759         
43760     },
43761
43762         // private
43763     onClick : function(e){ 
43764         //this.setChecked(!this.checked);
43765         Roo.get(e.target).toggleClass('x-menu-item-checked');
43766         this.refreshValue();
43767         //if(this.el.dom.checked != this.checked){
43768         //    this.setValue(this.el.dom.checked);
43769        // }
43770     },
43771     
43772     // private
43773     refreshValue : function()
43774     {
43775         var val = '';
43776         this.viewEl.select('img',true).each(function(e,i,n)  {
43777             val += e.is(".x-menu-item-checked") ? String(n) : '';
43778         });
43779         this.setValue(val, true);
43780     },
43781
43782     /**
43783      * Sets the checked state of the checkbox.
43784      * On is always based on a string comparison between inputValue and the param.
43785      * @param {Boolean/String} value - the value to set 
43786      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43787      */
43788     setValue : function(v,suppressEvent){
43789         if (!this.el.dom) {
43790             return;
43791         }
43792         var old = this.el.dom.value ;
43793         this.el.dom.value = v;
43794         if (suppressEvent) {
43795             return ;
43796         }
43797          
43798         // update display..
43799         this.viewEl.select('img',true).each(function(e,i,n)  {
43800             
43801             var on = e.is(".x-menu-item-checked");
43802             var newv = v.indexOf(String(n)) > -1;
43803             if (on != newv) {
43804                 e.toggleClass('x-menu-item-checked');
43805             }
43806             
43807         });
43808         
43809         
43810         this.fireEvent('change', this, v, old);
43811         
43812         
43813     },
43814    
43815     // handle setting of hidden value by some other method!!?!?
43816     setFromHidden: function()
43817     {
43818         if(!this.el){
43819             return;
43820         }
43821         //console.log("SET FROM HIDDEN");
43822         //alert('setFrom hidden');
43823         this.setValue(this.el.dom.value);
43824     },
43825     
43826     onDestroy : function()
43827     {
43828         if(this.viewEl){
43829             Roo.get(this.viewEl).remove();
43830         }
43831          
43832         Roo.form.DayPicker.superclass.onDestroy.call(this);
43833     }
43834
43835 });/*
43836  * RooJS Library 1.1.1
43837  * Copyright(c) 2008-2011  Alan Knowles
43838  *
43839  * License - LGPL
43840  */
43841  
43842
43843 /**
43844  * @class Roo.form.ComboCheck
43845  * @extends Roo.form.ComboBox
43846  * A combobox for multiple select items.
43847  *
43848  * FIXME - could do with a reset button..
43849  * 
43850  * @constructor
43851  * Create a new ComboCheck
43852  * @param {Object} config Configuration options
43853  */
43854 Roo.form.ComboCheck = function(config){
43855     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43856     // should verify some data...
43857     // like
43858     // hiddenName = required..
43859     // displayField = required
43860     // valudField == required
43861     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43862     var _t = this;
43863     Roo.each(req, function(e) {
43864         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43865             throw "Roo.form.ComboCheck : missing value for: " + e;
43866         }
43867     });
43868     
43869     
43870 };
43871
43872 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43873      
43874      
43875     editable : false,
43876      
43877     selectedClass: 'x-menu-item-checked', 
43878     
43879     // private
43880     onRender : function(ct, position){
43881         var _t = this;
43882         
43883         
43884         
43885         if(!this.tpl){
43886             var cls = 'x-combo-list';
43887
43888             
43889             this.tpl =  new Roo.Template({
43890                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43891                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43892                    '<span>{' + this.displayField + '}</span>' +
43893                     '</div>' 
43894                 
43895             });
43896         }
43897  
43898         
43899         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43900         this.view.singleSelect = false;
43901         this.view.multiSelect = true;
43902         this.view.toggleSelect = true;
43903         this.pageTb.add(new Roo.Toolbar.Fill(), {
43904             
43905             text: 'Done',
43906             handler: function()
43907             {
43908                 _t.collapse();
43909             }
43910         });
43911     },
43912     
43913     onViewOver : function(e, t){
43914         // do nothing...
43915         return;
43916         
43917     },
43918     
43919     onViewClick : function(doFocus,index){
43920         return;
43921         
43922     },
43923     select: function () {
43924         //Roo.log("SELECT CALLED");
43925     },
43926      
43927     selectByValue : function(xv, scrollIntoView){
43928         var ar = this.getValueArray();
43929         var sels = [];
43930         
43931         Roo.each(ar, function(v) {
43932             if(v === undefined || v === null){
43933                 return;
43934             }
43935             var r = this.findRecord(this.valueField, v);
43936             if(r){
43937                 sels.push(this.store.indexOf(r))
43938                 
43939             }
43940         },this);
43941         this.view.select(sels);
43942         return false;
43943     },
43944     
43945     
43946     
43947     onSelect : function(record, index){
43948        // Roo.log("onselect Called");
43949        // this is only called by the clear button now..
43950         this.view.clearSelections();
43951         this.setValue('[]');
43952         if (this.value != this.valueBefore) {
43953             this.fireEvent('change', this, this.value, this.valueBefore);
43954         }
43955     },
43956     getValueArray : function()
43957     {
43958         var ar = [] ;
43959         
43960         try {
43961             //Roo.log(this.value);
43962             if (typeof(this.value) == 'undefined') {
43963                 return [];
43964             }
43965             var ar = Roo.decode(this.value);
43966             return  ar instanceof Array ? ar : []; //?? valid?
43967             
43968         } catch(e) {
43969             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43970             return [];
43971         }
43972          
43973     },
43974     expand : function ()
43975     {
43976         Roo.form.ComboCheck.superclass.expand.call(this);
43977         this.valueBefore = this.value;
43978         
43979
43980     },
43981     
43982     collapse : function(){
43983         Roo.form.ComboCheck.superclass.collapse.call(this);
43984         var sl = this.view.getSelectedIndexes();
43985         var st = this.store;
43986         var nv = [];
43987         var tv = [];
43988         var r;
43989         Roo.each(sl, function(i) {
43990             r = st.getAt(i);
43991             nv.push(r.get(this.valueField));
43992         },this);
43993         this.setValue(Roo.encode(nv));
43994         if (this.value != this.valueBefore) {
43995
43996             this.fireEvent('change', this, this.value, this.valueBefore);
43997         }
43998         
43999     },
44000     
44001     setValue : function(v){
44002         // Roo.log(v);
44003         this.value = v;
44004         
44005         var vals = this.getValueArray();
44006         var tv = [];
44007         Roo.each(vals, function(k) {
44008             var r = this.findRecord(this.valueField, k);
44009             if(r){
44010                 tv.push(r.data[this.displayField]);
44011             }else if(this.valueNotFoundText !== undefined){
44012                 tv.push( this.valueNotFoundText );
44013             }
44014         },this);
44015        // Roo.log(tv);
44016         
44017         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
44018         this.hiddenField.value = v;
44019         this.value = v;
44020     }
44021     
44022 });//<script type="text/javasscript">
44023  
44024
44025 /**
44026  * @class Roo.DDView
44027  * A DnD enabled version of Roo.View.
44028  * @param {Element/String} container The Element in which to create the View.
44029  * @param {String} tpl The template string used to create the markup for each element of the View
44030  * @param {Object} config The configuration properties. These include all the config options of
44031  * {@link Roo.View} plus some specific to this class.<br>
44032  * <p>
44033  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
44034  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
44035  * <p>
44036  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
44037 .x-view-drag-insert-above {
44038         border-top:1px dotted #3366cc;
44039 }
44040 .x-view-drag-insert-below {
44041         border-bottom:1px dotted #3366cc;
44042 }
44043 </code></pre>
44044  * 
44045  */
44046  
44047 Roo.DDView = function(container, tpl, config) {
44048     Roo.DDView.superclass.constructor.apply(this, arguments);
44049     this.getEl().setStyle("outline", "0px none");
44050     this.getEl().unselectable();
44051     if (this.dragGroup) {
44052                 this.setDraggable(this.dragGroup.split(","));
44053     }
44054     if (this.dropGroup) {
44055                 this.setDroppable(this.dropGroup.split(","));
44056     }
44057     if (this.deletable) {
44058         this.setDeletable();
44059     }
44060     this.isDirtyFlag = false;
44061         this.addEvents({
44062                 "drop" : true
44063         });
44064 };
44065
44066 Roo.extend(Roo.DDView, Roo.View, {
44067 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
44068 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
44069 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
44070 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
44071
44072         isFormField: true,
44073
44074         reset: Roo.emptyFn,
44075         
44076         clearInvalid: Roo.form.Field.prototype.clearInvalid,
44077
44078         validate: function() {
44079                 return true;
44080         },
44081         
44082         destroy: function() {
44083                 this.purgeListeners();
44084                 this.getEl.removeAllListeners();
44085                 this.getEl().remove();
44086                 if (this.dragZone) {
44087                         if (this.dragZone.destroy) {
44088                                 this.dragZone.destroy();
44089                         }
44090                 }
44091                 if (this.dropZone) {
44092                         if (this.dropZone.destroy) {
44093                                 this.dropZone.destroy();
44094                         }
44095                 }
44096         },
44097
44098 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
44099         getName: function() {
44100                 return this.name;
44101         },
44102
44103 /**     Loads the View from a JSON string representing the Records to put into the Store. */
44104         setValue: function(v) {
44105                 if (!this.store) {
44106                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
44107                 }
44108                 var data = {};
44109                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
44110                 this.store.proxy = new Roo.data.MemoryProxy(data);
44111                 this.store.load();
44112         },
44113
44114 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
44115         getValue: function() {
44116                 var result = '(';
44117                 this.store.each(function(rec) {
44118                         result += rec.id + ',';
44119                 });
44120                 return result.substr(0, result.length - 1) + ')';
44121         },
44122         
44123         getIds: function() {
44124                 var i = 0, result = new Array(this.store.getCount());
44125                 this.store.each(function(rec) {
44126                         result[i++] = rec.id;
44127                 });
44128                 return result;
44129         },
44130         
44131         isDirty: function() {
44132                 return this.isDirtyFlag;
44133         },
44134
44135 /**
44136  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
44137  *      whole Element becomes the target, and this causes the drop gesture to append.
44138  */
44139     getTargetFromEvent : function(e) {
44140                 var target = e.getTarget();
44141                 while ((target !== null) && (target.parentNode != this.el.dom)) {
44142                 target = target.parentNode;
44143                 }
44144                 if (!target) {
44145                         target = this.el.dom.lastChild || this.el.dom;
44146                 }
44147                 return target;
44148     },
44149
44150 /**
44151  *      Create the drag data which consists of an object which has the property "ddel" as
44152  *      the drag proxy element. 
44153  */
44154     getDragData : function(e) {
44155         var target = this.findItemFromChild(e.getTarget());
44156                 if(target) {
44157                         this.handleSelection(e);
44158                         var selNodes = this.getSelectedNodes();
44159             var dragData = {
44160                 source: this,
44161                 copy: this.copy || (this.allowCopy && e.ctrlKey),
44162                 nodes: selNodes,
44163                 records: []
44164                         };
44165                         var selectedIndices = this.getSelectedIndexes();
44166                         for (var i = 0; i < selectedIndices.length; i++) {
44167                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
44168                         }
44169                         if (selNodes.length == 1) {
44170                                 dragData.ddel = target.cloneNode(true); // the div element
44171                         } else {
44172                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44173                                 div.className = 'multi-proxy';
44174                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44175                                         div.appendChild(selNodes[i].cloneNode(true));
44176                                 }
44177                                 dragData.ddel = div;
44178                         }
44179             //console.log(dragData)
44180             //console.log(dragData.ddel.innerHTML)
44181                         return dragData;
44182                 }
44183         //console.log('nodragData')
44184                 return false;
44185     },
44186     
44187 /**     Specify to which ddGroup items in this DDView may be dragged. */
44188     setDraggable: function(ddGroup) {
44189         if (ddGroup instanceof Array) {
44190                 Roo.each(ddGroup, this.setDraggable, this);
44191                 return;
44192         }
44193         if (this.dragZone) {
44194                 this.dragZone.addToGroup(ddGroup);
44195         } else {
44196                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44197                                 containerScroll: true,
44198                                 ddGroup: ddGroup 
44199
44200                         });
44201 //                      Draggability implies selection. DragZone's mousedown selects the element.
44202                         if (!this.multiSelect) { this.singleSelect = true; }
44203
44204 //                      Wire the DragZone's handlers up to methods in *this*
44205                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44206                 }
44207     },
44208
44209 /**     Specify from which ddGroup this DDView accepts drops. */
44210     setDroppable: function(ddGroup) {
44211         if (ddGroup instanceof Array) {
44212                 Roo.each(ddGroup, this.setDroppable, this);
44213                 return;
44214         }
44215         if (this.dropZone) {
44216                 this.dropZone.addToGroup(ddGroup);
44217         } else {
44218                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44219                                 containerScroll: true,
44220                                 ddGroup: ddGroup
44221                         });
44222
44223 //                      Wire the DropZone's handlers up to methods in *this*
44224                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44225                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44226                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44227                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44228                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44229                 }
44230     },
44231
44232 /**     Decide whether to drop above or below a View node. */
44233     getDropPoint : function(e, n, dd){
44234         if (n == this.el.dom) { return "above"; }
44235                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44236                 var c = t + (b - t) / 2;
44237                 var y = Roo.lib.Event.getPageY(e);
44238                 if(y <= c) {
44239                         return "above";
44240                 }else{
44241                         return "below";
44242                 }
44243     },
44244
44245     onNodeEnter : function(n, dd, e, data){
44246                 return false;
44247     },
44248     
44249     onNodeOver : function(n, dd, e, data){
44250                 var pt = this.getDropPoint(e, n, dd);
44251                 // set the insert point style on the target node
44252                 var dragElClass = this.dropNotAllowed;
44253                 if (pt) {
44254                         var targetElClass;
44255                         if (pt == "above"){
44256                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44257                                 targetElClass = "x-view-drag-insert-above";
44258                         } else {
44259                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44260                                 targetElClass = "x-view-drag-insert-below";
44261                         }
44262                         if (this.lastInsertClass != targetElClass){
44263                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44264                                 this.lastInsertClass = targetElClass;
44265                         }
44266                 }
44267                 return dragElClass;
44268         },
44269
44270     onNodeOut : function(n, dd, e, data){
44271                 this.removeDropIndicators(n);
44272     },
44273
44274     onNodeDrop : function(n, dd, e, data){
44275         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44276                 return false;
44277         }
44278         var pt = this.getDropPoint(e, n, dd);
44279                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44280                 if (pt == "below") { insertAt++; }
44281                 for (var i = 0; i < data.records.length; i++) {
44282                         var r = data.records[i];
44283                         var dup = this.store.getById(r.id);
44284                         if (dup && (dd != this.dragZone)) {
44285                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44286                         } else {
44287                                 if (data.copy) {
44288                                         this.store.insert(insertAt++, r.copy());
44289                                 } else {
44290                                         data.source.isDirtyFlag = true;
44291                                         r.store.remove(r);
44292                                         this.store.insert(insertAt++, r);
44293                                 }
44294                                 this.isDirtyFlag = true;
44295                         }
44296                 }
44297                 this.dragZone.cachedTarget = null;
44298                 return true;
44299     },
44300
44301     removeDropIndicators : function(n){
44302                 if(n){
44303                         Roo.fly(n).removeClass([
44304                                 "x-view-drag-insert-above",
44305                                 "x-view-drag-insert-below"]);
44306                         this.lastInsertClass = "_noclass";
44307                 }
44308     },
44309
44310 /**
44311  *      Utility method. Add a delete option to the DDView's context menu.
44312  *      @param {String} imageUrl The URL of the "delete" icon image.
44313  */
44314         setDeletable: function(imageUrl) {
44315                 if (!this.singleSelect && !this.multiSelect) {
44316                         this.singleSelect = true;
44317                 }
44318                 var c = this.getContextMenu();
44319                 this.contextMenu.on("itemclick", function(item) {
44320                         switch (item.id) {
44321                                 case "delete":
44322                                         this.remove(this.getSelectedIndexes());
44323                                         break;
44324                         }
44325                 }, this);
44326                 this.contextMenu.add({
44327                         icon: imageUrl,
44328                         id: "delete",
44329                         text: 'Delete'
44330                 });
44331         },
44332         
44333 /**     Return the context menu for this DDView. */
44334         getContextMenu: function() {
44335                 if (!this.contextMenu) {
44336 //                      Create the View's context menu
44337                         this.contextMenu = new Roo.menu.Menu({
44338                                 id: this.id + "-contextmenu"
44339                         });
44340                         this.el.on("contextmenu", this.showContextMenu, this);
44341                 }
44342                 return this.contextMenu;
44343         },
44344         
44345         disableContextMenu: function() {
44346                 if (this.contextMenu) {
44347                         this.el.un("contextmenu", this.showContextMenu, this);
44348                 }
44349         },
44350
44351         showContextMenu: function(e, item) {
44352         item = this.findItemFromChild(e.getTarget());
44353                 if (item) {
44354                         e.stopEvent();
44355                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44356                         this.contextMenu.showAt(e.getXY());
44357             }
44358     },
44359
44360 /**
44361  *      Remove {@link Roo.data.Record}s at the specified indices.
44362  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44363  */
44364     remove: function(selectedIndices) {
44365                 selectedIndices = [].concat(selectedIndices);
44366                 for (var i = 0; i < selectedIndices.length; i++) {
44367                         var rec = this.store.getAt(selectedIndices[i]);
44368                         this.store.remove(rec);
44369                 }
44370     },
44371
44372 /**
44373  *      Double click fires the event, but also, if this is draggable, and there is only one other
44374  *      related DropZone, it transfers the selected node.
44375  */
44376     onDblClick : function(e){
44377         var item = this.findItemFromChild(e.getTarget());
44378         if(item){
44379             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44380                 return false;
44381             }
44382             if (this.dragGroup) {
44383                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44384                     while (targets.indexOf(this.dropZone) > -1) {
44385                             targets.remove(this.dropZone);
44386                                 }
44387                     if (targets.length == 1) {
44388                                         this.dragZone.cachedTarget = null;
44389                         var el = Roo.get(targets[0].getEl());
44390                         var box = el.getBox(true);
44391                         targets[0].onNodeDrop(el.dom, {
44392                                 target: el.dom,
44393                                 xy: [box.x, box.y + box.height - 1]
44394                         }, null, this.getDragData(e));
44395                     }
44396                 }
44397         }
44398     },
44399     
44400     handleSelection: function(e) {
44401                 this.dragZone.cachedTarget = null;
44402         var item = this.findItemFromChild(e.getTarget());
44403         if (!item) {
44404                 this.clearSelections(true);
44405                 return;
44406         }
44407                 if (item && (this.multiSelect || this.singleSelect)){
44408                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44409                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44410                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44411                                 this.unselect(item);
44412                         } else {
44413                                 this.select(item, this.multiSelect && e.ctrlKey);
44414                                 this.lastSelection = item;
44415                         }
44416                 }
44417     },
44418
44419     onItemClick : function(item, index, e){
44420                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44421                         return false;
44422                 }
44423                 return true;
44424     },
44425
44426     unselect : function(nodeInfo, suppressEvent){
44427                 var node = this.getNode(nodeInfo);
44428                 if(node && this.isSelected(node)){
44429                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44430                                 Roo.fly(node).removeClass(this.selectedClass);
44431                                 this.selections.remove(node);
44432                                 if(!suppressEvent){
44433                                         this.fireEvent("selectionchange", this, this.selections);
44434                                 }
44435                         }
44436                 }
44437     }
44438 });
44439 /*
44440  * Based on:
44441  * Ext JS Library 1.1.1
44442  * Copyright(c) 2006-2007, Ext JS, LLC.
44443  *
44444  * Originally Released Under LGPL - original licence link has changed is not relivant.
44445  *
44446  * Fork - LGPL
44447  * <script type="text/javascript">
44448  */
44449  
44450 /**
44451  * @class Roo.LayoutManager
44452  * @extends Roo.util.Observable
44453  * Base class for layout managers.
44454  */
44455 Roo.LayoutManager = function(container, config){
44456     Roo.LayoutManager.superclass.constructor.call(this);
44457     this.el = Roo.get(container);
44458     // ie scrollbar fix
44459     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44460         document.body.scroll = "no";
44461     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44462         this.el.position('relative');
44463     }
44464     this.id = this.el.id;
44465     this.el.addClass("x-layout-container");
44466     /** false to disable window resize monitoring @type Boolean */
44467     this.monitorWindowResize = true;
44468     this.regions = {};
44469     this.addEvents({
44470         /**
44471          * @event layout
44472          * Fires when a layout is performed. 
44473          * @param {Roo.LayoutManager} this
44474          */
44475         "layout" : true,
44476         /**
44477          * @event regionresized
44478          * Fires when the user resizes a region. 
44479          * @param {Roo.LayoutRegion} region The resized region
44480          * @param {Number} newSize The new size (width for east/west, height for north/south)
44481          */
44482         "regionresized" : true,
44483         /**
44484          * @event regioncollapsed
44485          * Fires when a region is collapsed. 
44486          * @param {Roo.LayoutRegion} region The collapsed region
44487          */
44488         "regioncollapsed" : true,
44489         /**
44490          * @event regionexpanded
44491          * Fires when a region is expanded.  
44492          * @param {Roo.LayoutRegion} region The expanded region
44493          */
44494         "regionexpanded" : true
44495     });
44496     this.updating = false;
44497     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44498 };
44499
44500 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44501     /**
44502      * Returns true if this layout is currently being updated
44503      * @return {Boolean}
44504      */
44505     isUpdating : function(){
44506         return this.updating; 
44507     },
44508     
44509     /**
44510      * Suspend the LayoutManager from doing auto-layouts while
44511      * making multiple add or remove calls
44512      */
44513     beginUpdate : function(){
44514         this.updating = true;    
44515     },
44516     
44517     /**
44518      * Restore auto-layouts and optionally disable the manager from performing a layout
44519      * @param {Boolean} noLayout true to disable a layout update 
44520      */
44521     endUpdate : function(noLayout){
44522         this.updating = false;
44523         if(!noLayout){
44524             this.layout();
44525         }    
44526     },
44527     
44528     layout: function(){
44529         
44530     },
44531     
44532     onRegionResized : function(region, newSize){
44533         this.fireEvent("regionresized", region, newSize);
44534         this.layout();
44535     },
44536     
44537     onRegionCollapsed : function(region){
44538         this.fireEvent("regioncollapsed", region);
44539     },
44540     
44541     onRegionExpanded : function(region){
44542         this.fireEvent("regionexpanded", region);
44543     },
44544         
44545     /**
44546      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44547      * performs box-model adjustments.
44548      * @return {Object} The size as an object {width: (the width), height: (the height)}
44549      */
44550     getViewSize : function(){
44551         var size;
44552         if(this.el.dom != document.body){
44553             size = this.el.getSize();
44554         }else{
44555             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44556         }
44557         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44558         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44559         return size;
44560     },
44561     
44562     /**
44563      * Returns the Element this layout is bound to.
44564      * @return {Roo.Element}
44565      */
44566     getEl : function(){
44567         return this.el;
44568     },
44569     
44570     /**
44571      * Returns the specified region.
44572      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44573      * @return {Roo.LayoutRegion}
44574      */
44575     getRegion : function(target){
44576         return this.regions[target.toLowerCase()];
44577     },
44578     
44579     onWindowResize : function(){
44580         if(this.monitorWindowResize){
44581             this.layout();
44582         }
44583     }
44584 });/*
44585  * Based on:
44586  * Ext JS Library 1.1.1
44587  * Copyright(c) 2006-2007, Ext JS, LLC.
44588  *
44589  * Originally Released Under LGPL - original licence link has changed is not relivant.
44590  *
44591  * Fork - LGPL
44592  * <script type="text/javascript">
44593  */
44594 /**
44595  * @class Roo.BorderLayout
44596  * @extends Roo.LayoutManager
44597  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44598  * please see: <br><br>
44599  * <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>
44600  * <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>
44601  * Example:
44602  <pre><code>
44603  var layout = new Roo.BorderLayout(document.body, {
44604     north: {
44605         initialSize: 25,
44606         titlebar: false
44607     },
44608     west: {
44609         split:true,
44610         initialSize: 200,
44611         minSize: 175,
44612         maxSize: 400,
44613         titlebar: true,
44614         collapsible: true
44615     },
44616     east: {
44617         split:true,
44618         initialSize: 202,
44619         minSize: 175,
44620         maxSize: 400,
44621         titlebar: true,
44622         collapsible: true
44623     },
44624     south: {
44625         split:true,
44626         initialSize: 100,
44627         minSize: 100,
44628         maxSize: 200,
44629         titlebar: true,
44630         collapsible: true
44631     },
44632     center: {
44633         titlebar: true,
44634         autoScroll:true,
44635         resizeTabs: true,
44636         minTabWidth: 50,
44637         preferredTabWidth: 150
44638     }
44639 });
44640
44641 // shorthand
44642 var CP = Roo.ContentPanel;
44643
44644 layout.beginUpdate();
44645 layout.add("north", new CP("north", "North"));
44646 layout.add("south", new CP("south", {title: "South", closable: true}));
44647 layout.add("west", new CP("west", {title: "West"}));
44648 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44649 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44650 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44651 layout.getRegion("center").showPanel("center1");
44652 layout.endUpdate();
44653 </code></pre>
44654
44655 <b>The container the layout is rendered into can be either the body element or any other element.
44656 If it is not the body element, the container needs to either be an absolute positioned element,
44657 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44658 the container size if it is not the body element.</b>
44659
44660 * @constructor
44661 * Create a new BorderLayout
44662 * @param {String/HTMLElement/Element} container The container this layout is bound to
44663 * @param {Object} config Configuration options
44664  */
44665 Roo.BorderLayout = function(container, config){
44666     config = config || {};
44667     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44668     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44669     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44670         var target = this.factory.validRegions[i];
44671         if(config[target]){
44672             this.addRegion(target, config[target]);
44673         }
44674     }
44675 };
44676
44677 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44678     /**
44679      * Creates and adds a new region if it doesn't already exist.
44680      * @param {String} target The target region key (north, south, east, west or center).
44681      * @param {Object} config The regions config object
44682      * @return {BorderLayoutRegion} The new region
44683      */
44684     addRegion : function(target, config){
44685         if(!this.regions[target]){
44686             var r = this.factory.create(target, this, config);
44687             this.bindRegion(target, r);
44688         }
44689         return this.regions[target];
44690     },
44691
44692     // private (kinda)
44693     bindRegion : function(name, r){
44694         this.regions[name] = r;
44695         r.on("visibilitychange", this.layout, this);
44696         r.on("paneladded", this.layout, this);
44697         r.on("panelremoved", this.layout, this);
44698         r.on("invalidated", this.layout, this);
44699         r.on("resized", this.onRegionResized, this);
44700         r.on("collapsed", this.onRegionCollapsed, this);
44701         r.on("expanded", this.onRegionExpanded, this);
44702     },
44703
44704     /**
44705      * Performs a layout update.
44706      */
44707     layout : function(){
44708         if(this.updating) return;
44709         var size = this.getViewSize();
44710         var w = size.width;
44711         var h = size.height;
44712         var centerW = w;
44713         var centerH = h;
44714         var centerY = 0;
44715         var centerX = 0;
44716         //var x = 0, y = 0;
44717
44718         var rs = this.regions;
44719         var north = rs["north"];
44720         var south = rs["south"]; 
44721         var west = rs["west"];
44722         var east = rs["east"];
44723         var center = rs["center"];
44724         //if(this.hideOnLayout){ // not supported anymore
44725             //c.el.setStyle("display", "none");
44726         //}
44727         if(north && north.isVisible()){
44728             var b = north.getBox();
44729             var m = north.getMargins();
44730             b.width = w - (m.left+m.right);
44731             b.x = m.left;
44732             b.y = m.top;
44733             centerY = b.height + b.y + m.bottom;
44734             centerH -= centerY;
44735             north.updateBox(this.safeBox(b));
44736         }
44737         if(south && south.isVisible()){
44738             var b = south.getBox();
44739             var m = south.getMargins();
44740             b.width = w - (m.left+m.right);
44741             b.x = m.left;
44742             var totalHeight = (b.height + m.top + m.bottom);
44743             b.y = h - totalHeight + m.top;
44744             centerH -= totalHeight;
44745             south.updateBox(this.safeBox(b));
44746         }
44747         if(west && west.isVisible()){
44748             var b = west.getBox();
44749             var m = west.getMargins();
44750             b.height = centerH - (m.top+m.bottom);
44751             b.x = m.left;
44752             b.y = centerY + m.top;
44753             var totalWidth = (b.width + m.left + m.right);
44754             centerX += totalWidth;
44755             centerW -= totalWidth;
44756             west.updateBox(this.safeBox(b));
44757         }
44758         if(east && east.isVisible()){
44759             var b = east.getBox();
44760             var m = east.getMargins();
44761             b.height = centerH - (m.top+m.bottom);
44762             var totalWidth = (b.width + m.left + m.right);
44763             b.x = w - totalWidth + m.left;
44764             b.y = centerY + m.top;
44765             centerW -= totalWidth;
44766             east.updateBox(this.safeBox(b));
44767         }
44768         if(center){
44769             var m = center.getMargins();
44770             var centerBox = {
44771                 x: centerX + m.left,
44772                 y: centerY + m.top,
44773                 width: centerW - (m.left+m.right),
44774                 height: centerH - (m.top+m.bottom)
44775             };
44776             //if(this.hideOnLayout){
44777                 //center.el.setStyle("display", "block");
44778             //}
44779             center.updateBox(this.safeBox(centerBox));
44780         }
44781         this.el.repaint();
44782         this.fireEvent("layout", this);
44783     },
44784
44785     // private
44786     safeBox : function(box){
44787         box.width = Math.max(0, box.width);
44788         box.height = Math.max(0, box.height);
44789         return box;
44790     },
44791
44792     /**
44793      * Adds a ContentPanel (or subclass) to this layout.
44794      * @param {String} target The target region key (north, south, east, west or center).
44795      * @param {Roo.ContentPanel} panel The panel to add
44796      * @return {Roo.ContentPanel} The added panel
44797      */
44798     add : function(target, panel){
44799          
44800         target = target.toLowerCase();
44801         return this.regions[target].add(panel);
44802     },
44803
44804     /**
44805      * Remove a ContentPanel (or subclass) to this layout.
44806      * @param {String} target The target region key (north, south, east, west or center).
44807      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44808      * @return {Roo.ContentPanel} The removed panel
44809      */
44810     remove : function(target, panel){
44811         target = target.toLowerCase();
44812         return this.regions[target].remove(panel);
44813     },
44814
44815     /**
44816      * Searches all regions for a panel with the specified id
44817      * @param {String} panelId
44818      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44819      */
44820     findPanel : function(panelId){
44821         var rs = this.regions;
44822         for(var target in rs){
44823             if(typeof rs[target] != "function"){
44824                 var p = rs[target].getPanel(panelId);
44825                 if(p){
44826                     return p;
44827                 }
44828             }
44829         }
44830         return null;
44831     },
44832
44833     /**
44834      * Searches all regions for a panel with the specified id and activates (shows) it.
44835      * @param {String/ContentPanel} panelId The panels id or the panel itself
44836      * @return {Roo.ContentPanel} The shown panel or null
44837      */
44838     showPanel : function(panelId) {
44839       var rs = this.regions;
44840       for(var target in rs){
44841          var r = rs[target];
44842          if(typeof r != "function"){
44843             if(r.hasPanel(panelId)){
44844                return r.showPanel(panelId);
44845             }
44846          }
44847       }
44848       return null;
44849    },
44850
44851    /**
44852      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44853      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44854      */
44855     restoreState : function(provider){
44856         if(!provider){
44857             provider = Roo.state.Manager;
44858         }
44859         var sm = new Roo.LayoutStateManager();
44860         sm.init(this, provider);
44861     },
44862
44863     /**
44864      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44865      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44866      * a valid ContentPanel config object.  Example:
44867      * <pre><code>
44868 // Create the main layout
44869 var layout = new Roo.BorderLayout('main-ct', {
44870     west: {
44871         split:true,
44872         minSize: 175,
44873         titlebar: true
44874     },
44875     center: {
44876         title:'Components'
44877     }
44878 }, 'main-ct');
44879
44880 // Create and add multiple ContentPanels at once via configs
44881 layout.batchAdd({
44882    west: {
44883        id: 'source-files',
44884        autoCreate:true,
44885        title:'Ext Source Files',
44886        autoScroll:true,
44887        fitToFrame:true
44888    },
44889    center : {
44890        el: cview,
44891        autoScroll:true,
44892        fitToFrame:true,
44893        toolbar: tb,
44894        resizeEl:'cbody'
44895    }
44896 });
44897 </code></pre>
44898      * @param {Object} regions An object containing ContentPanel configs by region name
44899      */
44900     batchAdd : function(regions){
44901         this.beginUpdate();
44902         for(var rname in regions){
44903             var lr = this.regions[rname];
44904             if(lr){
44905                 this.addTypedPanels(lr, regions[rname]);
44906             }
44907         }
44908         this.endUpdate();
44909     },
44910
44911     // private
44912     addTypedPanels : function(lr, ps){
44913         if(typeof ps == 'string'){
44914             lr.add(new Roo.ContentPanel(ps));
44915         }
44916         else if(ps instanceof Array){
44917             for(var i =0, len = ps.length; i < len; i++){
44918                 this.addTypedPanels(lr, ps[i]);
44919             }
44920         }
44921         else if(!ps.events){ // raw config?
44922             var el = ps.el;
44923             delete ps.el; // prevent conflict
44924             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44925         }
44926         else {  // panel object assumed!
44927             lr.add(ps);
44928         }
44929     },
44930     /**
44931      * Adds a xtype elements to the layout.
44932      * <pre><code>
44933
44934 layout.addxtype({
44935        xtype : 'ContentPanel',
44936        region: 'west',
44937        items: [ .... ]
44938    }
44939 );
44940
44941 layout.addxtype({
44942         xtype : 'NestedLayoutPanel',
44943         region: 'west',
44944         layout: {
44945            center: { },
44946            west: { }   
44947         },
44948         items : [ ... list of content panels or nested layout panels.. ]
44949    }
44950 );
44951 </code></pre>
44952      * @param {Object} cfg Xtype definition of item to add.
44953      */
44954     addxtype : function(cfg)
44955     {
44956         // basically accepts a pannel...
44957         // can accept a layout region..!?!?
44958         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44959         
44960         if (!cfg.xtype.match(/Panel$/)) {
44961             return false;
44962         }
44963         var ret = false;
44964         
44965         if (typeof(cfg.region) == 'undefined') {
44966             Roo.log("Failed to add Panel, region was not set");
44967             Roo.log(cfg);
44968             return false;
44969         }
44970         var region = cfg.region;
44971         delete cfg.region;
44972         
44973           
44974         var xitems = [];
44975         if (cfg.items) {
44976             xitems = cfg.items;
44977             delete cfg.items;
44978         }
44979         var nb = false;
44980         
44981         switch(cfg.xtype) 
44982         {
44983             case 'ContentPanel':  // ContentPanel (el, cfg)
44984             case 'ScrollPanel':  // ContentPanel (el, cfg)
44985                 if(cfg.autoCreate) {
44986                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44987                 } else {
44988                     var el = this.el.createChild();
44989                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44990                 }
44991                 
44992                 this.add(region, ret);
44993                 break;
44994             
44995             
44996             case 'TreePanel': // our new panel!
44997                 cfg.el = this.el.createChild();
44998                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44999                 this.add(region, ret);
45000                 break;
45001             
45002             case 'NestedLayoutPanel': 
45003                 // create a new Layout (which is  a Border Layout...
45004                 var el = this.el.createChild();
45005                 var clayout = cfg.layout;
45006                 delete cfg.layout;
45007                 clayout.items   = clayout.items  || [];
45008                 // replace this exitems with the clayout ones..
45009                 xitems = clayout.items;
45010                  
45011                 
45012                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
45013                     cfg.background = false;
45014                 }
45015                 var layout = new Roo.BorderLayout(el, clayout);
45016                 
45017                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
45018                 //console.log('adding nested layout panel '  + cfg.toSource());
45019                 this.add(region, ret);
45020                 nb = {}; /// find first...
45021                 break;
45022                 
45023             case 'GridPanel': 
45024             
45025                 // needs grid and region
45026                 
45027                 //var el = this.getRegion(region).el.createChild();
45028                 var el = this.el.createChild();
45029                 // create the grid first...
45030                 
45031                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
45032                 delete cfg.grid;
45033                 if (region == 'center' && this.active ) {
45034                     cfg.background = false;
45035                 }
45036                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
45037                 
45038                 this.add(region, ret);
45039                 if (cfg.background) {
45040                     ret.on('activate', function(gp) {
45041                         if (!gp.grid.rendered) {
45042                             gp.grid.render();
45043                         }
45044                     });
45045                 } else {
45046                     grid.render();
45047                 }
45048                 break;
45049            
45050                
45051                 
45052                 
45053             default: 
45054                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
45055                 return null;
45056              // GridPanel (grid, cfg)
45057             
45058         }
45059         this.beginUpdate();
45060         // add children..
45061         var region = '';
45062         var abn = {};
45063         Roo.each(xitems, function(i)  {
45064             region = nb && i.region ? i.region : false;
45065             
45066             var add = ret.addxtype(i);
45067            
45068             if (region) {
45069                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
45070                 if (!i.background) {
45071                     abn[region] = nb[region] ;
45072                 }
45073             }
45074             
45075         });
45076         this.endUpdate();
45077
45078         // make the last non-background panel active..
45079         //if (nb) { Roo.log(abn); }
45080         if (nb) {
45081             
45082             for(var r in abn) {
45083                 region = this.getRegion(r);
45084                 if (region) {
45085                     // tried using nb[r], but it does not work..
45086                      
45087                     region.showPanel(abn[r]);
45088                    
45089                 }
45090             }
45091         }
45092         return ret;
45093         
45094     }
45095 });
45096
45097 /**
45098  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
45099  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
45100  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
45101  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
45102  * <pre><code>
45103 // shorthand
45104 var CP = Roo.ContentPanel;
45105
45106 var layout = Roo.BorderLayout.create({
45107     north: {
45108         initialSize: 25,
45109         titlebar: false,
45110         panels: [new CP("north", "North")]
45111     },
45112     west: {
45113         split:true,
45114         initialSize: 200,
45115         minSize: 175,
45116         maxSize: 400,
45117         titlebar: true,
45118         collapsible: true,
45119         panels: [new CP("west", {title: "West"})]
45120     },
45121     east: {
45122         split:true,
45123         initialSize: 202,
45124         minSize: 175,
45125         maxSize: 400,
45126         titlebar: true,
45127         collapsible: true,
45128         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
45129     },
45130     south: {
45131         split:true,
45132         initialSize: 100,
45133         minSize: 100,
45134         maxSize: 200,
45135         titlebar: true,
45136         collapsible: true,
45137         panels: [new CP("south", {title: "South", closable: true})]
45138     },
45139     center: {
45140         titlebar: true,
45141         autoScroll:true,
45142         resizeTabs: true,
45143         minTabWidth: 50,
45144         preferredTabWidth: 150,
45145         panels: [
45146             new CP("center1", {title: "Close Me", closable: true}),
45147             new CP("center2", {title: "Center Panel", closable: false})
45148         ]
45149     }
45150 }, document.body);
45151
45152 layout.getRegion("center").showPanel("center1");
45153 </code></pre>
45154  * @param config
45155  * @param targetEl
45156  */
45157 Roo.BorderLayout.create = function(config, targetEl){
45158     var layout = new Roo.BorderLayout(targetEl || document.body, config);
45159     layout.beginUpdate();
45160     var regions = Roo.BorderLayout.RegionFactory.validRegions;
45161     for(var j = 0, jlen = regions.length; j < jlen; j++){
45162         var lr = regions[j];
45163         if(layout.regions[lr] && config[lr].panels){
45164             var r = layout.regions[lr];
45165             var ps = config[lr].panels;
45166             layout.addTypedPanels(r, ps);
45167         }
45168     }
45169     layout.endUpdate();
45170     return layout;
45171 };
45172
45173 // private
45174 Roo.BorderLayout.RegionFactory = {
45175     // private
45176     validRegions : ["north","south","east","west","center"],
45177
45178     // private
45179     create : function(target, mgr, config){
45180         target = target.toLowerCase();
45181         if(config.lightweight || config.basic){
45182             return new Roo.BasicLayoutRegion(mgr, config, target);
45183         }
45184         switch(target){
45185             case "north":
45186                 return new Roo.NorthLayoutRegion(mgr, config);
45187             case "south":
45188                 return new Roo.SouthLayoutRegion(mgr, config);
45189             case "east":
45190                 return new Roo.EastLayoutRegion(mgr, config);
45191             case "west":
45192                 return new Roo.WestLayoutRegion(mgr, config);
45193             case "center":
45194                 return new Roo.CenterLayoutRegion(mgr, config);
45195         }
45196         throw 'Layout region "'+target+'" not supported.';
45197     }
45198 };/*
45199  * Based on:
45200  * Ext JS Library 1.1.1
45201  * Copyright(c) 2006-2007, Ext JS, LLC.
45202  *
45203  * Originally Released Under LGPL - original licence link has changed is not relivant.
45204  *
45205  * Fork - LGPL
45206  * <script type="text/javascript">
45207  */
45208  
45209 /**
45210  * @class Roo.BasicLayoutRegion
45211  * @extends Roo.util.Observable
45212  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45213  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45214  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45215  */
45216 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45217     this.mgr = mgr;
45218     this.position  = pos;
45219     this.events = {
45220         /**
45221          * @scope Roo.BasicLayoutRegion
45222          */
45223         
45224         /**
45225          * @event beforeremove
45226          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45227          * @param {Roo.LayoutRegion} this
45228          * @param {Roo.ContentPanel} panel The panel
45229          * @param {Object} e The cancel event object
45230          */
45231         "beforeremove" : true,
45232         /**
45233          * @event invalidated
45234          * Fires when the layout for this region is changed.
45235          * @param {Roo.LayoutRegion} this
45236          */
45237         "invalidated" : true,
45238         /**
45239          * @event visibilitychange
45240          * Fires when this region is shown or hidden 
45241          * @param {Roo.LayoutRegion} this
45242          * @param {Boolean} visibility true or false
45243          */
45244         "visibilitychange" : true,
45245         /**
45246          * @event paneladded
45247          * Fires when a panel is added. 
45248          * @param {Roo.LayoutRegion} this
45249          * @param {Roo.ContentPanel} panel The panel
45250          */
45251         "paneladded" : true,
45252         /**
45253          * @event panelremoved
45254          * Fires when a panel is removed. 
45255          * @param {Roo.LayoutRegion} this
45256          * @param {Roo.ContentPanel} panel The panel
45257          */
45258         "panelremoved" : true,
45259         /**
45260          * @event collapsed
45261          * Fires when this region is collapsed.
45262          * @param {Roo.LayoutRegion} this
45263          */
45264         "collapsed" : true,
45265         /**
45266          * @event expanded
45267          * Fires when this region is expanded.
45268          * @param {Roo.LayoutRegion} this
45269          */
45270         "expanded" : true,
45271         /**
45272          * @event slideshow
45273          * Fires when this region is slid into view.
45274          * @param {Roo.LayoutRegion} this
45275          */
45276         "slideshow" : true,
45277         /**
45278          * @event slidehide
45279          * Fires when this region slides out of view. 
45280          * @param {Roo.LayoutRegion} this
45281          */
45282         "slidehide" : true,
45283         /**
45284          * @event panelactivated
45285          * Fires when a panel is activated. 
45286          * @param {Roo.LayoutRegion} this
45287          * @param {Roo.ContentPanel} panel The activated panel
45288          */
45289         "panelactivated" : true,
45290         /**
45291          * @event resized
45292          * Fires when the user resizes this region. 
45293          * @param {Roo.LayoutRegion} this
45294          * @param {Number} newSize The new size (width for east/west, height for north/south)
45295          */
45296         "resized" : true
45297     };
45298     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45299     this.panels = new Roo.util.MixedCollection();
45300     this.panels.getKey = this.getPanelId.createDelegate(this);
45301     this.box = null;
45302     this.activePanel = null;
45303     // ensure listeners are added...
45304     
45305     if (config.listeners || config.events) {
45306         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45307             listeners : config.listeners || {},
45308             events : config.events || {}
45309         });
45310     }
45311     
45312     if(skipConfig !== true){
45313         this.applyConfig(config);
45314     }
45315 };
45316
45317 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45318     getPanelId : function(p){
45319         return p.getId();
45320     },
45321     
45322     applyConfig : function(config){
45323         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45324         this.config = config;
45325         
45326     },
45327     
45328     /**
45329      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45330      * the width, for horizontal (north, south) the height.
45331      * @param {Number} newSize The new width or height
45332      */
45333     resizeTo : function(newSize){
45334         var el = this.el ? this.el :
45335                  (this.activePanel ? this.activePanel.getEl() : null);
45336         if(el){
45337             switch(this.position){
45338                 case "east":
45339                 case "west":
45340                     el.setWidth(newSize);
45341                     this.fireEvent("resized", this, newSize);
45342                 break;
45343                 case "north":
45344                 case "south":
45345                     el.setHeight(newSize);
45346                     this.fireEvent("resized", this, newSize);
45347                 break;                
45348             }
45349         }
45350     },
45351     
45352     getBox : function(){
45353         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45354     },
45355     
45356     getMargins : function(){
45357         return this.margins;
45358     },
45359     
45360     updateBox : function(box){
45361         this.box = box;
45362         var el = this.activePanel.getEl();
45363         el.dom.style.left = box.x + "px";
45364         el.dom.style.top = box.y + "px";
45365         this.activePanel.setSize(box.width, box.height);
45366     },
45367     
45368     /**
45369      * Returns the container element for this region.
45370      * @return {Roo.Element}
45371      */
45372     getEl : function(){
45373         return this.activePanel;
45374     },
45375     
45376     /**
45377      * Returns true if this region is currently visible.
45378      * @return {Boolean}
45379      */
45380     isVisible : function(){
45381         return this.activePanel ? true : false;
45382     },
45383     
45384     setActivePanel : function(panel){
45385         panel = this.getPanel(panel);
45386         if(this.activePanel && this.activePanel != panel){
45387             this.activePanel.setActiveState(false);
45388             this.activePanel.getEl().setLeftTop(-10000,-10000);
45389         }
45390         this.activePanel = panel;
45391         panel.setActiveState(true);
45392         if(this.box){
45393             panel.setSize(this.box.width, this.box.height);
45394         }
45395         this.fireEvent("panelactivated", this, panel);
45396         this.fireEvent("invalidated");
45397     },
45398     
45399     /**
45400      * Show the specified panel.
45401      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45402      * @return {Roo.ContentPanel} The shown panel or null
45403      */
45404     showPanel : function(panel){
45405         if(panel = this.getPanel(panel)){
45406             this.setActivePanel(panel);
45407         }
45408         return panel;
45409     },
45410     
45411     /**
45412      * Get the active panel for this region.
45413      * @return {Roo.ContentPanel} The active panel or null
45414      */
45415     getActivePanel : function(){
45416         return this.activePanel;
45417     },
45418     
45419     /**
45420      * Add the passed ContentPanel(s)
45421      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45422      * @return {Roo.ContentPanel} The panel added (if only one was added)
45423      */
45424     add : function(panel){
45425         if(arguments.length > 1){
45426             for(var i = 0, len = arguments.length; i < len; i++) {
45427                 this.add(arguments[i]);
45428             }
45429             return null;
45430         }
45431         if(this.hasPanel(panel)){
45432             this.showPanel(panel);
45433             return panel;
45434         }
45435         var el = panel.getEl();
45436         if(el.dom.parentNode != this.mgr.el.dom){
45437             this.mgr.el.dom.appendChild(el.dom);
45438         }
45439         if(panel.setRegion){
45440             panel.setRegion(this);
45441         }
45442         this.panels.add(panel);
45443         el.setStyle("position", "absolute");
45444         if(!panel.background){
45445             this.setActivePanel(panel);
45446             if(this.config.initialSize && this.panels.getCount()==1){
45447                 this.resizeTo(this.config.initialSize);
45448             }
45449         }
45450         this.fireEvent("paneladded", this, panel);
45451         return panel;
45452     },
45453     
45454     /**
45455      * Returns true if the panel is in this region.
45456      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45457      * @return {Boolean}
45458      */
45459     hasPanel : function(panel){
45460         if(typeof panel == "object"){ // must be panel obj
45461             panel = panel.getId();
45462         }
45463         return this.getPanel(panel) ? true : false;
45464     },
45465     
45466     /**
45467      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45468      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45469      * @param {Boolean} preservePanel Overrides the config preservePanel option
45470      * @return {Roo.ContentPanel} The panel that was removed
45471      */
45472     remove : function(panel, preservePanel){
45473         panel = this.getPanel(panel);
45474         if(!panel){
45475             return null;
45476         }
45477         var e = {};
45478         this.fireEvent("beforeremove", this, panel, e);
45479         if(e.cancel === true){
45480             return null;
45481         }
45482         var panelId = panel.getId();
45483         this.panels.removeKey(panelId);
45484         return panel;
45485     },
45486     
45487     /**
45488      * Returns the panel specified or null if it's not in this region.
45489      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45490      * @return {Roo.ContentPanel}
45491      */
45492     getPanel : function(id){
45493         if(typeof id == "object"){ // must be panel obj
45494             return id;
45495         }
45496         return this.panels.get(id);
45497     },
45498     
45499     /**
45500      * Returns this regions position (north/south/east/west/center).
45501      * @return {String} 
45502      */
45503     getPosition: function(){
45504         return this.position;    
45505     }
45506 });/*
45507  * Based on:
45508  * Ext JS Library 1.1.1
45509  * Copyright(c) 2006-2007, Ext JS, LLC.
45510  *
45511  * Originally Released Under LGPL - original licence link has changed is not relivant.
45512  *
45513  * Fork - LGPL
45514  * <script type="text/javascript">
45515  */
45516  
45517 /**
45518  * @class Roo.LayoutRegion
45519  * @extends Roo.BasicLayoutRegion
45520  * This class represents a region in a layout manager.
45521  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45522  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45523  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45524  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45525  * @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})
45526  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45527  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45528  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45529  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45530  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45531  * @cfg {String}    title           The title for the region (overrides panel titles)
45532  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45533  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45534  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45535  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45536  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45537  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45538  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45539  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45540  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45541  * @cfg {Boolean}   showPin         True to show a pin button
45542  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45543  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45544  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45545  * @cfg {Number}    width           For East/West panels
45546  * @cfg {Number}    height          For North/South panels
45547  * @cfg {Boolean}   split           To show the splitter
45548  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45549  */
45550 Roo.LayoutRegion = function(mgr, config, pos){
45551     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45552     var dh = Roo.DomHelper;
45553     /** This region's container element 
45554     * @type Roo.Element */
45555     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45556     /** This region's title element 
45557     * @type Roo.Element */
45558
45559     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45560         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45561         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45562     ]}, true);
45563     this.titleEl.enableDisplayMode();
45564     /** This region's title text element 
45565     * @type HTMLElement */
45566     this.titleTextEl = this.titleEl.dom.firstChild;
45567     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45568     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45569     this.closeBtn.enableDisplayMode();
45570     this.closeBtn.on("click", this.closeClicked, this);
45571     this.closeBtn.hide();
45572
45573     this.createBody(config);
45574     this.visible = true;
45575     this.collapsed = false;
45576
45577     if(config.hideWhenEmpty){
45578         this.hide();
45579         this.on("paneladded", this.validateVisibility, this);
45580         this.on("panelremoved", this.validateVisibility, this);
45581     }
45582     this.applyConfig(config);
45583 };
45584
45585 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45586
45587     createBody : function(){
45588         /** This region's body element 
45589         * @type Roo.Element */
45590         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45591     },
45592
45593     applyConfig : function(c){
45594         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45595             var dh = Roo.DomHelper;
45596             if(c.titlebar !== false){
45597                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45598                 this.collapseBtn.on("click", this.collapse, this);
45599                 this.collapseBtn.enableDisplayMode();
45600
45601                 if(c.showPin === true || this.showPin){
45602                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45603                     this.stickBtn.enableDisplayMode();
45604                     this.stickBtn.on("click", this.expand, this);
45605                     this.stickBtn.hide();
45606                 }
45607             }
45608             /** This region's collapsed element
45609             * @type Roo.Element */
45610             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45611                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45612             ]}, true);
45613             if(c.floatable !== false){
45614                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45615                this.collapsedEl.on("click", this.collapseClick, this);
45616             }
45617
45618             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45619                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45620                    id: "message", unselectable: "on", style:{"float":"left"}});
45621                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45622              }
45623             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45624             this.expandBtn.on("click", this.expand, this);
45625         }
45626         if(this.collapseBtn){
45627             this.collapseBtn.setVisible(c.collapsible == true);
45628         }
45629         this.cmargins = c.cmargins || this.cmargins ||
45630                          (this.position == "west" || this.position == "east" ?
45631                              {top: 0, left: 2, right:2, bottom: 0} :
45632                              {top: 2, left: 0, right:0, bottom: 2});
45633         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45634         this.bottomTabs = c.tabPosition != "top";
45635         this.autoScroll = c.autoScroll || false;
45636         if(this.autoScroll){
45637             this.bodyEl.setStyle("overflow", "auto");
45638         }else{
45639             this.bodyEl.setStyle("overflow", "hidden");
45640         }
45641         //if(c.titlebar !== false){
45642             if((!c.titlebar && !c.title) || c.titlebar === false){
45643                 this.titleEl.hide();
45644             }else{
45645                 this.titleEl.show();
45646                 if(c.title){
45647                     this.titleTextEl.innerHTML = c.title;
45648                 }
45649             }
45650         //}
45651         this.duration = c.duration || .30;
45652         this.slideDuration = c.slideDuration || .45;
45653         this.config = c;
45654         if(c.collapsed){
45655             this.collapse(true);
45656         }
45657         if(c.hidden){
45658             this.hide();
45659         }
45660     },
45661     /**
45662      * Returns true if this region is currently visible.
45663      * @return {Boolean}
45664      */
45665     isVisible : function(){
45666         return this.visible;
45667     },
45668
45669     /**
45670      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45671      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45672      */
45673     setCollapsedTitle : function(title){
45674         title = title || "&#160;";
45675         if(this.collapsedTitleTextEl){
45676             this.collapsedTitleTextEl.innerHTML = title;
45677         }
45678     },
45679
45680     getBox : function(){
45681         var b;
45682         if(!this.collapsed){
45683             b = this.el.getBox(false, true);
45684         }else{
45685             b = this.collapsedEl.getBox(false, true);
45686         }
45687         return b;
45688     },
45689
45690     getMargins : function(){
45691         return this.collapsed ? this.cmargins : this.margins;
45692     },
45693
45694     highlight : function(){
45695         this.el.addClass("x-layout-panel-dragover");
45696     },
45697
45698     unhighlight : function(){
45699         this.el.removeClass("x-layout-panel-dragover");
45700     },
45701
45702     updateBox : function(box){
45703         this.box = box;
45704         if(!this.collapsed){
45705             this.el.dom.style.left = box.x + "px";
45706             this.el.dom.style.top = box.y + "px";
45707             this.updateBody(box.width, box.height);
45708         }else{
45709             this.collapsedEl.dom.style.left = box.x + "px";
45710             this.collapsedEl.dom.style.top = box.y + "px";
45711             this.collapsedEl.setSize(box.width, box.height);
45712         }
45713         if(this.tabs){
45714             this.tabs.autoSizeTabs();
45715         }
45716     },
45717
45718     updateBody : function(w, h){
45719         if(w !== null){
45720             this.el.setWidth(w);
45721             w -= this.el.getBorderWidth("rl");
45722             if(this.config.adjustments){
45723                 w += this.config.adjustments[0];
45724             }
45725         }
45726         if(h !== null){
45727             this.el.setHeight(h);
45728             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45729             h -= this.el.getBorderWidth("tb");
45730             if(this.config.adjustments){
45731                 h += this.config.adjustments[1];
45732             }
45733             this.bodyEl.setHeight(h);
45734             if(this.tabs){
45735                 h = this.tabs.syncHeight(h);
45736             }
45737         }
45738         if(this.panelSize){
45739             w = w !== null ? w : this.panelSize.width;
45740             h = h !== null ? h : this.panelSize.height;
45741         }
45742         if(this.activePanel){
45743             var el = this.activePanel.getEl();
45744             w = w !== null ? w : el.getWidth();
45745             h = h !== null ? h : el.getHeight();
45746             this.panelSize = {width: w, height: h};
45747             this.activePanel.setSize(w, h);
45748         }
45749         if(Roo.isIE && this.tabs){
45750             this.tabs.el.repaint();
45751         }
45752     },
45753
45754     /**
45755      * Returns the container element for this region.
45756      * @return {Roo.Element}
45757      */
45758     getEl : function(){
45759         return this.el;
45760     },
45761
45762     /**
45763      * Hides this region.
45764      */
45765     hide : function(){
45766         if(!this.collapsed){
45767             this.el.dom.style.left = "-2000px";
45768             this.el.hide();
45769         }else{
45770             this.collapsedEl.dom.style.left = "-2000px";
45771             this.collapsedEl.hide();
45772         }
45773         this.visible = false;
45774         this.fireEvent("visibilitychange", this, false);
45775     },
45776
45777     /**
45778      * Shows this region if it was previously hidden.
45779      */
45780     show : function(){
45781         if(!this.collapsed){
45782             this.el.show();
45783         }else{
45784             this.collapsedEl.show();
45785         }
45786         this.visible = true;
45787         this.fireEvent("visibilitychange", this, true);
45788     },
45789
45790     closeClicked : function(){
45791         if(this.activePanel){
45792             this.remove(this.activePanel);
45793         }
45794     },
45795
45796     collapseClick : function(e){
45797         if(this.isSlid){
45798            e.stopPropagation();
45799            this.slideIn();
45800         }else{
45801            e.stopPropagation();
45802            this.slideOut();
45803         }
45804     },
45805
45806     /**
45807      * Collapses this region.
45808      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45809      */
45810     collapse : function(skipAnim){
45811         if(this.collapsed) return;
45812         this.collapsed = true;
45813         if(this.split){
45814             this.split.el.hide();
45815         }
45816         if(this.config.animate && skipAnim !== true){
45817             this.fireEvent("invalidated", this);
45818             this.animateCollapse();
45819         }else{
45820             this.el.setLocation(-20000,-20000);
45821             this.el.hide();
45822             this.collapsedEl.show();
45823             this.fireEvent("collapsed", this);
45824             this.fireEvent("invalidated", this);
45825         }
45826     },
45827
45828     animateCollapse : function(){
45829         // overridden
45830     },
45831
45832     /**
45833      * Expands this region if it was previously collapsed.
45834      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45835      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45836      */
45837     expand : function(e, skipAnim){
45838         if(e) e.stopPropagation();
45839         if(!this.collapsed || this.el.hasActiveFx()) return;
45840         if(this.isSlid){
45841             this.afterSlideIn();
45842             skipAnim = true;
45843         }
45844         this.collapsed = false;
45845         if(this.config.animate && skipAnim !== true){
45846             this.animateExpand();
45847         }else{
45848             this.el.show();
45849             if(this.split){
45850                 this.split.el.show();
45851             }
45852             this.collapsedEl.setLocation(-2000,-2000);
45853             this.collapsedEl.hide();
45854             this.fireEvent("invalidated", this);
45855             this.fireEvent("expanded", this);
45856         }
45857     },
45858
45859     animateExpand : function(){
45860         // overridden
45861     },
45862
45863     initTabs : function()
45864     {
45865         this.bodyEl.setStyle("overflow", "hidden");
45866         var ts = new Roo.TabPanel(
45867                 this.bodyEl.dom,
45868                 {
45869                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45870                     disableTooltips: this.config.disableTabTips,
45871                     toolbar : this.config.toolbar
45872                 }
45873         );
45874         if(this.config.hideTabs){
45875             ts.stripWrap.setDisplayed(false);
45876         }
45877         this.tabs = ts;
45878         ts.resizeTabs = this.config.resizeTabs === true;
45879         ts.minTabWidth = this.config.minTabWidth || 40;
45880         ts.maxTabWidth = this.config.maxTabWidth || 250;
45881         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45882         ts.monitorResize = false;
45883         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45884         ts.bodyEl.addClass('x-layout-tabs-body');
45885         this.panels.each(this.initPanelAsTab, this);
45886     },
45887
45888     initPanelAsTab : function(panel){
45889         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45890                     this.config.closeOnTab && panel.isClosable());
45891         if(panel.tabTip !== undefined){
45892             ti.setTooltip(panel.tabTip);
45893         }
45894         ti.on("activate", function(){
45895               this.setActivePanel(panel);
45896         }, this);
45897         if(this.config.closeOnTab){
45898             ti.on("beforeclose", function(t, e){
45899                 e.cancel = true;
45900                 this.remove(panel);
45901             }, this);
45902         }
45903         return ti;
45904     },
45905
45906     updatePanelTitle : function(panel, title){
45907         if(this.activePanel == panel){
45908             this.updateTitle(title);
45909         }
45910         if(this.tabs){
45911             var ti = this.tabs.getTab(panel.getEl().id);
45912             ti.setText(title);
45913             if(panel.tabTip !== undefined){
45914                 ti.setTooltip(panel.tabTip);
45915             }
45916         }
45917     },
45918
45919     updateTitle : function(title){
45920         if(this.titleTextEl && !this.config.title){
45921             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45922         }
45923     },
45924
45925     setActivePanel : function(panel){
45926         panel = this.getPanel(panel);
45927         if(this.activePanel && this.activePanel != panel){
45928             this.activePanel.setActiveState(false);
45929         }
45930         this.activePanel = panel;
45931         panel.setActiveState(true);
45932         if(this.panelSize){
45933             panel.setSize(this.panelSize.width, this.panelSize.height);
45934         }
45935         if(this.closeBtn){
45936             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45937         }
45938         this.updateTitle(panel.getTitle());
45939         if(this.tabs){
45940             this.fireEvent("invalidated", this);
45941         }
45942         this.fireEvent("panelactivated", this, panel);
45943     },
45944
45945     /**
45946      * Shows the specified panel.
45947      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45948      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45949      */
45950     showPanel : function(panel){
45951         if(panel = this.getPanel(panel)){
45952             if(this.tabs){
45953                 var tab = this.tabs.getTab(panel.getEl().id);
45954                 if(tab.isHidden()){
45955                     this.tabs.unhideTab(tab.id);
45956                 }
45957                 tab.activate();
45958             }else{
45959                 this.setActivePanel(panel);
45960             }
45961         }
45962         return panel;
45963     },
45964
45965     /**
45966      * Get the active panel for this region.
45967      * @return {Roo.ContentPanel} The active panel or null
45968      */
45969     getActivePanel : function(){
45970         return this.activePanel;
45971     },
45972
45973     validateVisibility : function(){
45974         if(this.panels.getCount() < 1){
45975             this.updateTitle("&#160;");
45976             this.closeBtn.hide();
45977             this.hide();
45978         }else{
45979             if(!this.isVisible()){
45980                 this.show();
45981             }
45982         }
45983     },
45984
45985     /**
45986      * Adds the passed ContentPanel(s) to this region.
45987      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45988      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45989      */
45990     add : function(panel){
45991         if(arguments.length > 1){
45992             for(var i = 0, len = arguments.length; i < len; i++) {
45993                 this.add(arguments[i]);
45994             }
45995             return null;
45996         }
45997         if(this.hasPanel(panel)){
45998             this.showPanel(panel);
45999             return panel;
46000         }
46001         panel.setRegion(this);
46002         this.panels.add(panel);
46003         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
46004             this.bodyEl.dom.appendChild(panel.getEl().dom);
46005             if(panel.background !== true){
46006                 this.setActivePanel(panel);
46007             }
46008             this.fireEvent("paneladded", this, panel);
46009             return panel;
46010         }
46011         if(!this.tabs){
46012             this.initTabs();
46013         }else{
46014             this.initPanelAsTab(panel);
46015         }
46016         if(panel.background !== true){
46017             this.tabs.activate(panel.getEl().id);
46018         }
46019         this.fireEvent("paneladded", this, panel);
46020         return panel;
46021     },
46022
46023     /**
46024      * Hides the tab for the specified panel.
46025      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46026      */
46027     hidePanel : function(panel){
46028         if(this.tabs && (panel = this.getPanel(panel))){
46029             this.tabs.hideTab(panel.getEl().id);
46030         }
46031     },
46032
46033     /**
46034      * Unhides the tab for a previously hidden panel.
46035      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46036      */
46037     unhidePanel : function(panel){
46038         if(this.tabs && (panel = this.getPanel(panel))){
46039             this.tabs.unhideTab(panel.getEl().id);
46040         }
46041     },
46042
46043     clearPanels : function(){
46044         while(this.panels.getCount() > 0){
46045              this.remove(this.panels.first());
46046         }
46047     },
46048
46049     /**
46050      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46051      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46052      * @param {Boolean} preservePanel Overrides the config preservePanel option
46053      * @return {Roo.ContentPanel} The panel that was removed
46054      */
46055     remove : function(panel, preservePanel){
46056         panel = this.getPanel(panel);
46057         if(!panel){
46058             return null;
46059         }
46060         var e = {};
46061         this.fireEvent("beforeremove", this, panel, e);
46062         if(e.cancel === true){
46063             return null;
46064         }
46065         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
46066         var panelId = panel.getId();
46067         this.panels.removeKey(panelId);
46068         if(preservePanel){
46069             document.body.appendChild(panel.getEl().dom);
46070         }
46071         if(this.tabs){
46072             this.tabs.removeTab(panel.getEl().id);
46073         }else if (!preservePanel){
46074             this.bodyEl.dom.removeChild(panel.getEl().dom);
46075         }
46076         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
46077             var p = this.panels.first();
46078             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
46079             tempEl.appendChild(p.getEl().dom);
46080             this.bodyEl.update("");
46081             this.bodyEl.dom.appendChild(p.getEl().dom);
46082             tempEl = null;
46083             this.updateTitle(p.getTitle());
46084             this.tabs = null;
46085             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46086             this.setActivePanel(p);
46087         }
46088         panel.setRegion(null);
46089         if(this.activePanel == panel){
46090             this.activePanel = null;
46091         }
46092         if(this.config.autoDestroy !== false && preservePanel !== true){
46093             try{panel.destroy();}catch(e){}
46094         }
46095         this.fireEvent("panelremoved", this, panel);
46096         return panel;
46097     },
46098
46099     /**
46100      * Returns the TabPanel component used by this region
46101      * @return {Roo.TabPanel}
46102      */
46103     getTabs : function(){
46104         return this.tabs;
46105     },
46106
46107     createTool : function(parentEl, className){
46108         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
46109             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
46110         btn.addClassOnOver("x-layout-tools-button-over");
46111         return btn;
46112     }
46113 });/*
46114  * Based on:
46115  * Ext JS Library 1.1.1
46116  * Copyright(c) 2006-2007, Ext JS, LLC.
46117  *
46118  * Originally Released Under LGPL - original licence link has changed is not relivant.
46119  *
46120  * Fork - LGPL
46121  * <script type="text/javascript">
46122  */
46123  
46124
46125
46126 /**
46127  * @class Roo.SplitLayoutRegion
46128  * @extends Roo.LayoutRegion
46129  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
46130  */
46131 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
46132     this.cursor = cursor;
46133     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
46134 };
46135
46136 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
46137     splitTip : "Drag to resize.",
46138     collapsibleSplitTip : "Drag to resize. Double click to hide.",
46139     useSplitTips : false,
46140
46141     applyConfig : function(config){
46142         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
46143         if(config.split){
46144             if(!this.split){
46145                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
46146                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
46147                 /** The SplitBar for this region 
46148                 * @type Roo.SplitBar */
46149                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
46150                 this.split.on("moved", this.onSplitMove, this);
46151                 this.split.useShim = config.useShim === true;
46152                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
46153                 if(this.useSplitTips){
46154                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
46155                 }
46156                 if(config.collapsible){
46157                     this.split.el.on("dblclick", this.collapse,  this);
46158                 }
46159             }
46160             if(typeof config.minSize != "undefined"){
46161                 this.split.minSize = config.minSize;
46162             }
46163             if(typeof config.maxSize != "undefined"){
46164                 this.split.maxSize = config.maxSize;
46165             }
46166             if(config.hideWhenEmpty || config.hidden || config.collapsed){
46167                 this.hideSplitter();
46168             }
46169         }
46170     },
46171
46172     getHMaxSize : function(){
46173          var cmax = this.config.maxSize || 10000;
46174          var center = this.mgr.getRegion("center");
46175          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46176     },
46177
46178     getVMaxSize : function(){
46179          var cmax = this.config.maxSize || 10000;
46180          var center = this.mgr.getRegion("center");
46181          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46182     },
46183
46184     onSplitMove : function(split, newSize){
46185         this.fireEvent("resized", this, newSize);
46186     },
46187     
46188     /** 
46189      * Returns the {@link Roo.SplitBar} for this region.
46190      * @return {Roo.SplitBar}
46191      */
46192     getSplitBar : function(){
46193         return this.split;
46194     },
46195     
46196     hide : function(){
46197         this.hideSplitter();
46198         Roo.SplitLayoutRegion.superclass.hide.call(this);
46199     },
46200
46201     hideSplitter : function(){
46202         if(this.split){
46203             this.split.el.setLocation(-2000,-2000);
46204             this.split.el.hide();
46205         }
46206     },
46207
46208     show : function(){
46209         if(this.split){
46210             this.split.el.show();
46211         }
46212         Roo.SplitLayoutRegion.superclass.show.call(this);
46213     },
46214     
46215     beforeSlide: function(){
46216         if(Roo.isGecko){// firefox overflow auto bug workaround
46217             this.bodyEl.clip();
46218             if(this.tabs) this.tabs.bodyEl.clip();
46219             if(this.activePanel){
46220                 this.activePanel.getEl().clip();
46221                 
46222                 if(this.activePanel.beforeSlide){
46223                     this.activePanel.beforeSlide();
46224                 }
46225             }
46226         }
46227     },
46228     
46229     afterSlide : function(){
46230         if(Roo.isGecko){// firefox overflow auto bug workaround
46231             this.bodyEl.unclip();
46232             if(this.tabs) this.tabs.bodyEl.unclip();
46233             if(this.activePanel){
46234                 this.activePanel.getEl().unclip();
46235                 if(this.activePanel.afterSlide){
46236                     this.activePanel.afterSlide();
46237                 }
46238             }
46239         }
46240     },
46241
46242     initAutoHide : function(){
46243         if(this.autoHide !== false){
46244             if(!this.autoHideHd){
46245                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46246                 this.autoHideHd = {
46247                     "mouseout": function(e){
46248                         if(!e.within(this.el, true)){
46249                             st.delay(500);
46250                         }
46251                     },
46252                     "mouseover" : function(e){
46253                         st.cancel();
46254                     },
46255                     scope : this
46256                 };
46257             }
46258             this.el.on(this.autoHideHd);
46259         }
46260     },
46261
46262     clearAutoHide : function(){
46263         if(this.autoHide !== false){
46264             this.el.un("mouseout", this.autoHideHd.mouseout);
46265             this.el.un("mouseover", this.autoHideHd.mouseover);
46266         }
46267     },
46268
46269     clearMonitor : function(){
46270         Roo.get(document).un("click", this.slideInIf, this);
46271     },
46272
46273     // these names are backwards but not changed for compat
46274     slideOut : function(){
46275         if(this.isSlid || this.el.hasActiveFx()){
46276             return;
46277         }
46278         this.isSlid = true;
46279         if(this.collapseBtn){
46280             this.collapseBtn.hide();
46281         }
46282         this.closeBtnState = this.closeBtn.getStyle('display');
46283         this.closeBtn.hide();
46284         if(this.stickBtn){
46285             this.stickBtn.show();
46286         }
46287         this.el.show();
46288         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46289         this.beforeSlide();
46290         this.el.setStyle("z-index", 10001);
46291         this.el.slideIn(this.getSlideAnchor(), {
46292             callback: function(){
46293                 this.afterSlide();
46294                 this.initAutoHide();
46295                 Roo.get(document).on("click", this.slideInIf, this);
46296                 this.fireEvent("slideshow", this);
46297             },
46298             scope: this,
46299             block: true
46300         });
46301     },
46302
46303     afterSlideIn : function(){
46304         this.clearAutoHide();
46305         this.isSlid = false;
46306         this.clearMonitor();
46307         this.el.setStyle("z-index", "");
46308         if(this.collapseBtn){
46309             this.collapseBtn.show();
46310         }
46311         this.closeBtn.setStyle('display', this.closeBtnState);
46312         if(this.stickBtn){
46313             this.stickBtn.hide();
46314         }
46315         this.fireEvent("slidehide", this);
46316     },
46317
46318     slideIn : function(cb){
46319         if(!this.isSlid || this.el.hasActiveFx()){
46320             Roo.callback(cb);
46321             return;
46322         }
46323         this.isSlid = false;
46324         this.beforeSlide();
46325         this.el.slideOut(this.getSlideAnchor(), {
46326             callback: function(){
46327                 this.el.setLeftTop(-10000, -10000);
46328                 this.afterSlide();
46329                 this.afterSlideIn();
46330                 Roo.callback(cb);
46331             },
46332             scope: this,
46333             block: true
46334         });
46335     },
46336     
46337     slideInIf : function(e){
46338         if(!e.within(this.el)){
46339             this.slideIn();
46340         }
46341     },
46342
46343     animateCollapse : function(){
46344         this.beforeSlide();
46345         this.el.setStyle("z-index", 20000);
46346         var anchor = this.getSlideAnchor();
46347         this.el.slideOut(anchor, {
46348             callback : function(){
46349                 this.el.setStyle("z-index", "");
46350                 this.collapsedEl.slideIn(anchor, {duration:.3});
46351                 this.afterSlide();
46352                 this.el.setLocation(-10000,-10000);
46353                 this.el.hide();
46354                 this.fireEvent("collapsed", this);
46355             },
46356             scope: this,
46357             block: true
46358         });
46359     },
46360
46361     animateExpand : function(){
46362         this.beforeSlide();
46363         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46364         this.el.setStyle("z-index", 20000);
46365         this.collapsedEl.hide({
46366             duration:.1
46367         });
46368         this.el.slideIn(this.getSlideAnchor(), {
46369             callback : function(){
46370                 this.el.setStyle("z-index", "");
46371                 this.afterSlide();
46372                 if(this.split){
46373                     this.split.el.show();
46374                 }
46375                 this.fireEvent("invalidated", this);
46376                 this.fireEvent("expanded", this);
46377             },
46378             scope: this,
46379             block: true
46380         });
46381     },
46382
46383     anchors : {
46384         "west" : "left",
46385         "east" : "right",
46386         "north" : "top",
46387         "south" : "bottom"
46388     },
46389
46390     sanchors : {
46391         "west" : "l",
46392         "east" : "r",
46393         "north" : "t",
46394         "south" : "b"
46395     },
46396
46397     canchors : {
46398         "west" : "tl-tr",
46399         "east" : "tr-tl",
46400         "north" : "tl-bl",
46401         "south" : "bl-tl"
46402     },
46403
46404     getAnchor : function(){
46405         return this.anchors[this.position];
46406     },
46407
46408     getCollapseAnchor : function(){
46409         return this.canchors[this.position];
46410     },
46411
46412     getSlideAnchor : function(){
46413         return this.sanchors[this.position];
46414     },
46415
46416     getAlignAdj : function(){
46417         var cm = this.cmargins;
46418         switch(this.position){
46419             case "west":
46420                 return [0, 0];
46421             break;
46422             case "east":
46423                 return [0, 0];
46424             break;
46425             case "north":
46426                 return [0, 0];
46427             break;
46428             case "south":
46429                 return [0, 0];
46430             break;
46431         }
46432     },
46433
46434     getExpandAdj : function(){
46435         var c = this.collapsedEl, cm = this.cmargins;
46436         switch(this.position){
46437             case "west":
46438                 return [-(cm.right+c.getWidth()+cm.left), 0];
46439             break;
46440             case "east":
46441                 return [cm.right+c.getWidth()+cm.left, 0];
46442             break;
46443             case "north":
46444                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46445             break;
46446             case "south":
46447                 return [0, cm.top+cm.bottom+c.getHeight()];
46448             break;
46449         }
46450     }
46451 });/*
46452  * Based on:
46453  * Ext JS Library 1.1.1
46454  * Copyright(c) 2006-2007, Ext JS, LLC.
46455  *
46456  * Originally Released Under LGPL - original licence link has changed is not relivant.
46457  *
46458  * Fork - LGPL
46459  * <script type="text/javascript">
46460  */
46461 /*
46462  * These classes are private internal classes
46463  */
46464 Roo.CenterLayoutRegion = function(mgr, config){
46465     Roo.LayoutRegion.call(this, mgr, config, "center");
46466     this.visible = true;
46467     this.minWidth = config.minWidth || 20;
46468     this.minHeight = config.minHeight || 20;
46469 };
46470
46471 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46472     hide : function(){
46473         // center panel can't be hidden
46474     },
46475     
46476     show : function(){
46477         // center panel can't be hidden
46478     },
46479     
46480     getMinWidth: function(){
46481         return this.minWidth;
46482     },
46483     
46484     getMinHeight: function(){
46485         return this.minHeight;
46486     }
46487 });
46488
46489
46490 Roo.NorthLayoutRegion = function(mgr, config){
46491     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46492     if(this.split){
46493         this.split.placement = Roo.SplitBar.TOP;
46494         this.split.orientation = Roo.SplitBar.VERTICAL;
46495         this.split.el.addClass("x-layout-split-v");
46496     }
46497     var size = config.initialSize || config.height;
46498     if(typeof size != "undefined"){
46499         this.el.setHeight(size);
46500     }
46501 };
46502 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46503     orientation: Roo.SplitBar.VERTICAL,
46504     getBox : function(){
46505         if(this.collapsed){
46506             return this.collapsedEl.getBox();
46507         }
46508         var box = this.el.getBox();
46509         if(this.split){
46510             box.height += this.split.el.getHeight();
46511         }
46512         return box;
46513     },
46514     
46515     updateBox : function(box){
46516         if(this.split && !this.collapsed){
46517             box.height -= this.split.el.getHeight();
46518             this.split.el.setLeft(box.x);
46519             this.split.el.setTop(box.y+box.height);
46520             this.split.el.setWidth(box.width);
46521         }
46522         if(this.collapsed){
46523             this.updateBody(box.width, null);
46524         }
46525         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46526     }
46527 });
46528
46529 Roo.SouthLayoutRegion = function(mgr, config){
46530     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46531     if(this.split){
46532         this.split.placement = Roo.SplitBar.BOTTOM;
46533         this.split.orientation = Roo.SplitBar.VERTICAL;
46534         this.split.el.addClass("x-layout-split-v");
46535     }
46536     var size = config.initialSize || config.height;
46537     if(typeof size != "undefined"){
46538         this.el.setHeight(size);
46539     }
46540 };
46541 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46542     orientation: Roo.SplitBar.VERTICAL,
46543     getBox : function(){
46544         if(this.collapsed){
46545             return this.collapsedEl.getBox();
46546         }
46547         var box = this.el.getBox();
46548         if(this.split){
46549             var sh = this.split.el.getHeight();
46550             box.height += sh;
46551             box.y -= sh;
46552         }
46553         return box;
46554     },
46555     
46556     updateBox : function(box){
46557         if(this.split && !this.collapsed){
46558             var sh = this.split.el.getHeight();
46559             box.height -= sh;
46560             box.y += sh;
46561             this.split.el.setLeft(box.x);
46562             this.split.el.setTop(box.y-sh);
46563             this.split.el.setWidth(box.width);
46564         }
46565         if(this.collapsed){
46566             this.updateBody(box.width, null);
46567         }
46568         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46569     }
46570 });
46571
46572 Roo.EastLayoutRegion = function(mgr, config){
46573     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46574     if(this.split){
46575         this.split.placement = Roo.SplitBar.RIGHT;
46576         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46577         this.split.el.addClass("x-layout-split-h");
46578     }
46579     var size = config.initialSize || config.width;
46580     if(typeof size != "undefined"){
46581         this.el.setWidth(size);
46582     }
46583 };
46584 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46585     orientation: Roo.SplitBar.HORIZONTAL,
46586     getBox : function(){
46587         if(this.collapsed){
46588             return this.collapsedEl.getBox();
46589         }
46590         var box = this.el.getBox();
46591         if(this.split){
46592             var sw = this.split.el.getWidth();
46593             box.width += sw;
46594             box.x -= sw;
46595         }
46596         return box;
46597     },
46598
46599     updateBox : function(box){
46600         if(this.split && !this.collapsed){
46601             var sw = this.split.el.getWidth();
46602             box.width -= sw;
46603             this.split.el.setLeft(box.x);
46604             this.split.el.setTop(box.y);
46605             this.split.el.setHeight(box.height);
46606             box.x += sw;
46607         }
46608         if(this.collapsed){
46609             this.updateBody(null, box.height);
46610         }
46611         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46612     }
46613 });
46614
46615 Roo.WestLayoutRegion = function(mgr, config){
46616     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46617     if(this.split){
46618         this.split.placement = Roo.SplitBar.LEFT;
46619         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46620         this.split.el.addClass("x-layout-split-h");
46621     }
46622     var size = config.initialSize || config.width;
46623     if(typeof size != "undefined"){
46624         this.el.setWidth(size);
46625     }
46626 };
46627 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46628     orientation: Roo.SplitBar.HORIZONTAL,
46629     getBox : function(){
46630         if(this.collapsed){
46631             return this.collapsedEl.getBox();
46632         }
46633         var box = this.el.getBox();
46634         if(this.split){
46635             box.width += this.split.el.getWidth();
46636         }
46637         return box;
46638     },
46639     
46640     updateBox : function(box){
46641         if(this.split && !this.collapsed){
46642             var sw = this.split.el.getWidth();
46643             box.width -= sw;
46644             this.split.el.setLeft(box.x+box.width);
46645             this.split.el.setTop(box.y);
46646             this.split.el.setHeight(box.height);
46647         }
46648         if(this.collapsed){
46649             this.updateBody(null, box.height);
46650         }
46651         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46652     }
46653 });
46654 /*
46655  * Based on:
46656  * Ext JS Library 1.1.1
46657  * Copyright(c) 2006-2007, Ext JS, LLC.
46658  *
46659  * Originally Released Under LGPL - original licence link has changed is not relivant.
46660  *
46661  * Fork - LGPL
46662  * <script type="text/javascript">
46663  */
46664  
46665  
46666 /*
46667  * Private internal class for reading and applying state
46668  */
46669 Roo.LayoutStateManager = function(layout){
46670      // default empty state
46671      this.state = {
46672         north: {},
46673         south: {},
46674         east: {},
46675         west: {}       
46676     };
46677 };
46678
46679 Roo.LayoutStateManager.prototype = {
46680     init : function(layout, provider){
46681         this.provider = provider;
46682         var state = provider.get(layout.id+"-layout-state");
46683         if(state){
46684             var wasUpdating = layout.isUpdating();
46685             if(!wasUpdating){
46686                 layout.beginUpdate();
46687             }
46688             for(var key in state){
46689                 if(typeof state[key] != "function"){
46690                     var rstate = state[key];
46691                     var r = layout.getRegion(key);
46692                     if(r && rstate){
46693                         if(rstate.size){
46694                             r.resizeTo(rstate.size);
46695                         }
46696                         if(rstate.collapsed == true){
46697                             r.collapse(true);
46698                         }else{
46699                             r.expand(null, true);
46700                         }
46701                     }
46702                 }
46703             }
46704             if(!wasUpdating){
46705                 layout.endUpdate();
46706             }
46707             this.state = state; 
46708         }
46709         this.layout = layout;
46710         layout.on("regionresized", this.onRegionResized, this);
46711         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46712         layout.on("regionexpanded", this.onRegionExpanded, this);
46713     },
46714     
46715     storeState : function(){
46716         this.provider.set(this.layout.id+"-layout-state", this.state);
46717     },
46718     
46719     onRegionResized : function(region, newSize){
46720         this.state[region.getPosition()].size = newSize;
46721         this.storeState();
46722     },
46723     
46724     onRegionCollapsed : function(region){
46725         this.state[region.getPosition()].collapsed = true;
46726         this.storeState();
46727     },
46728     
46729     onRegionExpanded : function(region){
46730         this.state[region.getPosition()].collapsed = false;
46731         this.storeState();
46732     }
46733 };/*
46734  * Based on:
46735  * Ext JS Library 1.1.1
46736  * Copyright(c) 2006-2007, Ext JS, LLC.
46737  *
46738  * Originally Released Under LGPL - original licence link has changed is not relivant.
46739  *
46740  * Fork - LGPL
46741  * <script type="text/javascript">
46742  */
46743 /**
46744  * @class Roo.ContentPanel
46745  * @extends Roo.util.Observable
46746  * A basic ContentPanel element.
46747  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46748  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46749  * @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
46750  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46751  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46752  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46753  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46754  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46755  * @cfg {String} title          The title for this panel
46756  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46757  * @cfg {String} url            Calls {@link #setUrl} with this value
46758  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46759  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46760  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46761  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46762
46763  * @constructor
46764  * Create a new ContentPanel.
46765  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46766  * @param {String/Object} config A string to set only the title or a config object
46767  * @param {String} content (optional) Set the HTML content for this panel
46768  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46769  */
46770 Roo.ContentPanel = function(el, config, content){
46771     
46772      
46773     /*
46774     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46775         config = el;
46776         el = Roo.id();
46777     }
46778     if (config && config.parentLayout) { 
46779         el = config.parentLayout.el.createChild(); 
46780     }
46781     */
46782     if(el.autoCreate){ // xtype is available if this is called from factory
46783         config = el;
46784         el = Roo.id();
46785     }
46786     this.el = Roo.get(el);
46787     if(!this.el && config && config.autoCreate){
46788         if(typeof config.autoCreate == "object"){
46789             if(!config.autoCreate.id){
46790                 config.autoCreate.id = config.id||el;
46791             }
46792             this.el = Roo.DomHelper.append(document.body,
46793                         config.autoCreate, true);
46794         }else{
46795             this.el = Roo.DomHelper.append(document.body,
46796                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46797         }
46798     }
46799     this.closable = false;
46800     this.loaded = false;
46801     this.active = false;
46802     if(typeof config == "string"){
46803         this.title = config;
46804     }else{
46805         Roo.apply(this, config);
46806     }
46807     
46808     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46809         this.wrapEl = this.el.wrap();
46810         this.toolbar.container = this.el.insertSibling(false, 'before');
46811         this.toolbar = new Roo.Toolbar(this.toolbar);
46812     }
46813     
46814     
46815     
46816     if(this.resizeEl){
46817         this.resizeEl = Roo.get(this.resizeEl, true);
46818     }else{
46819         this.resizeEl = this.el;
46820     }
46821     this.addEvents({
46822         /**
46823          * @event activate
46824          * Fires when this panel is activated. 
46825          * @param {Roo.ContentPanel} this
46826          */
46827         "activate" : true,
46828         /**
46829          * @event deactivate
46830          * Fires when this panel is activated. 
46831          * @param {Roo.ContentPanel} this
46832          */
46833         "deactivate" : true,
46834
46835         /**
46836          * @event resize
46837          * Fires when this panel is resized if fitToFrame is true.
46838          * @param {Roo.ContentPanel} this
46839          * @param {Number} width The width after any component adjustments
46840          * @param {Number} height The height after any component adjustments
46841          */
46842         "resize" : true,
46843         
46844          /**
46845          * @event render
46846          * Fires when this tab is created
46847          * @param {Roo.ContentPanel} this
46848          */
46849         "render" : true
46850         
46851         
46852         
46853     });
46854     if(this.autoScroll){
46855         this.resizeEl.setStyle("overflow", "auto");
46856     } else {
46857         // fix randome scrolling
46858         this.el.on('scroll', function() {
46859             Roo.log('fix random scolling');
46860             this.scrollTo('top',0); 
46861         });
46862     }
46863     content = content || this.content;
46864     if(content){
46865         this.setContent(content);
46866     }
46867     if(config && config.url){
46868         this.setUrl(this.url, this.params, this.loadOnce);
46869     }
46870     
46871     
46872     
46873     Roo.ContentPanel.superclass.constructor.call(this);
46874     
46875     this.fireEvent('render', this);
46876 };
46877
46878 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46879     tabTip:'',
46880     setRegion : function(region){
46881         this.region = region;
46882         if(region){
46883            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46884         }else{
46885            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46886         } 
46887     },
46888     
46889     /**
46890      * Returns the toolbar for this Panel if one was configured. 
46891      * @return {Roo.Toolbar} 
46892      */
46893     getToolbar : function(){
46894         return this.toolbar;
46895     },
46896     
46897     setActiveState : function(active){
46898         this.active = active;
46899         if(!active){
46900             this.fireEvent("deactivate", this);
46901         }else{
46902             this.fireEvent("activate", this);
46903         }
46904     },
46905     /**
46906      * Updates this panel's element
46907      * @param {String} content The new content
46908      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46909     */
46910     setContent : function(content, loadScripts){
46911         this.el.update(content, loadScripts);
46912     },
46913
46914     ignoreResize : function(w, h){
46915         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46916             return true;
46917         }else{
46918             this.lastSize = {width: w, height: h};
46919             return false;
46920         }
46921     },
46922     /**
46923      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46924      * @return {Roo.UpdateManager} The UpdateManager
46925      */
46926     getUpdateManager : function(){
46927         return this.el.getUpdateManager();
46928     },
46929      /**
46930      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46931      * @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:
46932 <pre><code>
46933 panel.load({
46934     url: "your-url.php",
46935     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46936     callback: yourFunction,
46937     scope: yourObject, //(optional scope)
46938     discardUrl: false,
46939     nocache: false,
46940     text: "Loading...",
46941     timeout: 30,
46942     scripts: false
46943 });
46944 </code></pre>
46945      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46946      * 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.
46947      * @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}
46948      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46949      * @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.
46950      * @return {Roo.ContentPanel} this
46951      */
46952     load : function(){
46953         var um = this.el.getUpdateManager();
46954         um.update.apply(um, arguments);
46955         return this;
46956     },
46957
46958
46959     /**
46960      * 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.
46961      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46962      * @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)
46963      * @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)
46964      * @return {Roo.UpdateManager} The UpdateManager
46965      */
46966     setUrl : function(url, params, loadOnce){
46967         if(this.refreshDelegate){
46968             this.removeListener("activate", this.refreshDelegate);
46969         }
46970         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46971         this.on("activate", this.refreshDelegate);
46972         return this.el.getUpdateManager();
46973     },
46974     
46975     _handleRefresh : function(url, params, loadOnce){
46976         if(!loadOnce || !this.loaded){
46977             var updater = this.el.getUpdateManager();
46978             updater.update(url, params, this._setLoaded.createDelegate(this));
46979         }
46980     },
46981     
46982     _setLoaded : function(){
46983         this.loaded = true;
46984     }, 
46985     
46986     /**
46987      * Returns this panel's id
46988      * @return {String} 
46989      */
46990     getId : function(){
46991         return this.el.id;
46992     },
46993     
46994     /** 
46995      * Returns this panel's element - used by regiosn to add.
46996      * @return {Roo.Element} 
46997      */
46998     getEl : function(){
46999         return this.wrapEl || this.el;
47000     },
47001     
47002     adjustForComponents : function(width, height){
47003         if(this.resizeEl != this.el){
47004             width -= this.el.getFrameWidth('lr');
47005             height -= this.el.getFrameWidth('tb');
47006         }
47007         if(this.toolbar){
47008             var te = this.toolbar.getEl();
47009             height -= te.getHeight();
47010             te.setWidth(width);
47011         }
47012         if(this.adjustments){
47013             width += this.adjustments[0];
47014             height += this.adjustments[1];
47015         }
47016         return {"width": width, "height": height};
47017     },
47018     
47019     setSize : function(width, height){
47020         if(this.fitToFrame && !this.ignoreResize(width, height)){
47021             if(this.fitContainer && this.resizeEl != this.el){
47022                 this.el.setSize(width, height);
47023             }
47024             var size = this.adjustForComponents(width, height);
47025             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
47026             this.fireEvent('resize', this, size.width, size.height);
47027         }
47028     },
47029     
47030     /**
47031      * Returns this panel's title
47032      * @return {String} 
47033      */
47034     getTitle : function(){
47035         return this.title;
47036     },
47037     
47038     /**
47039      * Set this panel's title
47040      * @param {String} title
47041      */
47042     setTitle : function(title){
47043         this.title = title;
47044         if(this.region){
47045             this.region.updatePanelTitle(this, title);
47046         }
47047     },
47048     
47049     /**
47050      * Returns true is this panel was configured to be closable
47051      * @return {Boolean} 
47052      */
47053     isClosable : function(){
47054         return this.closable;
47055     },
47056     
47057     beforeSlide : function(){
47058         this.el.clip();
47059         this.resizeEl.clip();
47060     },
47061     
47062     afterSlide : function(){
47063         this.el.unclip();
47064         this.resizeEl.unclip();
47065     },
47066     
47067     /**
47068      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
47069      *   Will fail silently if the {@link #setUrl} method has not been called.
47070      *   This does not activate the panel, just updates its content.
47071      */
47072     refresh : function(){
47073         if(this.refreshDelegate){
47074            this.loaded = false;
47075            this.refreshDelegate();
47076         }
47077     },
47078     
47079     /**
47080      * Destroys this panel
47081      */
47082     destroy : function(){
47083         this.el.removeAllListeners();
47084         var tempEl = document.createElement("span");
47085         tempEl.appendChild(this.el.dom);
47086         tempEl.innerHTML = "";
47087         this.el.remove();
47088         this.el = null;
47089     },
47090     
47091     /**
47092      * form - if the content panel contains a form - this is a reference to it.
47093      * @type {Roo.form.Form}
47094      */
47095     form : false,
47096     /**
47097      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
47098      *    This contains a reference to it.
47099      * @type {Roo.View}
47100      */
47101     view : false,
47102     
47103       /**
47104      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
47105      * <pre><code>
47106
47107 layout.addxtype({
47108        xtype : 'Form',
47109        items: [ .... ]
47110    }
47111 );
47112
47113 </code></pre>
47114      * @param {Object} cfg Xtype definition of item to add.
47115      */
47116     
47117     addxtype : function(cfg) {
47118         // add form..
47119         if (cfg.xtype.match(/^Form$/)) {
47120             var el = this.el.createChild();
47121
47122             this.form = new  Roo.form.Form(cfg);
47123             
47124             
47125             if ( this.form.allItems.length) this.form.render(el.dom);
47126             return this.form;
47127         }
47128         // should only have one of theses..
47129         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
47130             // views..
47131             cfg.el = this.el.appendChild(document.createElement("div"));
47132             // factory?
47133             
47134             var ret = new Roo.factory(cfg);
47135             ret.render && ret.render(false, ''); // render blank..
47136             this.view = ret;
47137             return ret;
47138         }
47139         return false;
47140     }
47141 });
47142
47143 /**
47144  * @class Roo.GridPanel
47145  * @extends Roo.ContentPanel
47146  * @constructor
47147  * Create a new GridPanel.
47148  * @param {Roo.grid.Grid} grid The grid for this panel
47149  * @param {String/Object} config A string to set only the panel's title, or a config object
47150  */
47151 Roo.GridPanel = function(grid, config){
47152     
47153   
47154     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
47155         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
47156         
47157     this.wrapper.dom.appendChild(grid.getGridEl().dom);
47158     
47159     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
47160     
47161     if(this.toolbar){
47162         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
47163     }
47164     // xtype created footer. - not sure if will work as we normally have to render first..
47165     if (this.footer && !this.footer.el && this.footer.xtype) {
47166         
47167         this.footer.container = this.grid.getView().getFooterPanel(true);
47168         this.footer.dataSource = this.grid.dataSource;
47169         this.footer = Roo.factory(this.footer, Roo);
47170         
47171     }
47172     
47173     grid.monitorWindowResize = false; // turn off autosizing
47174     grid.autoHeight = false;
47175     grid.autoWidth = false;
47176     this.grid = grid;
47177     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47178 };
47179
47180 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47181     getId : function(){
47182         return this.grid.id;
47183     },
47184     
47185     /**
47186      * Returns the grid for this panel
47187      * @return {Roo.grid.Grid} 
47188      */
47189     getGrid : function(){
47190         return this.grid;    
47191     },
47192     
47193     setSize : function(width, height){
47194         if(!this.ignoreResize(width, height)){
47195             var grid = this.grid;
47196             var size = this.adjustForComponents(width, height);
47197             grid.getGridEl().setSize(size.width, size.height);
47198             grid.autoSize();
47199         }
47200     },
47201     
47202     beforeSlide : function(){
47203         this.grid.getView().scroller.clip();
47204     },
47205     
47206     afterSlide : function(){
47207         this.grid.getView().scroller.unclip();
47208     },
47209     
47210     destroy : function(){
47211         this.grid.destroy();
47212         delete this.grid;
47213         Roo.GridPanel.superclass.destroy.call(this); 
47214     }
47215 });
47216
47217
47218 /**
47219  * @class Roo.NestedLayoutPanel
47220  * @extends Roo.ContentPanel
47221  * @constructor
47222  * Create a new NestedLayoutPanel.
47223  * 
47224  * 
47225  * @param {Roo.BorderLayout} layout The layout for this panel
47226  * @param {String/Object} config A string to set only the title or a config object
47227  */
47228 Roo.NestedLayoutPanel = function(layout, config)
47229 {
47230     // construct with only one argument..
47231     /* FIXME - implement nicer consturctors
47232     if (layout.layout) {
47233         config = layout;
47234         layout = config.layout;
47235         delete config.layout;
47236     }
47237     if (layout.xtype && !layout.getEl) {
47238         // then layout needs constructing..
47239         layout = Roo.factory(layout, Roo);
47240     }
47241     */
47242     
47243     
47244     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47245     
47246     layout.monitorWindowResize = false; // turn off autosizing
47247     this.layout = layout;
47248     this.layout.getEl().addClass("x-layout-nested-layout");
47249     
47250     
47251     
47252     
47253 };
47254
47255 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47256
47257     setSize : function(width, height){
47258         if(!this.ignoreResize(width, height)){
47259             var size = this.adjustForComponents(width, height);
47260             var el = this.layout.getEl();
47261             el.setSize(size.width, size.height);
47262             var touch = el.dom.offsetWidth;
47263             this.layout.layout();
47264             // ie requires a double layout on the first pass
47265             if(Roo.isIE && !this.initialized){
47266                 this.initialized = true;
47267                 this.layout.layout();
47268             }
47269         }
47270     },
47271     
47272     // activate all subpanels if not currently active..
47273     
47274     setActiveState : function(active){
47275         this.active = active;
47276         if(!active){
47277             this.fireEvent("deactivate", this);
47278             return;
47279         }
47280         
47281         this.fireEvent("activate", this);
47282         // not sure if this should happen before or after..
47283         if (!this.layout) {
47284             return; // should not happen..
47285         }
47286         var reg = false;
47287         for (var r in this.layout.regions) {
47288             reg = this.layout.getRegion(r);
47289             if (reg.getActivePanel()) {
47290                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47291                 reg.setActivePanel(reg.getActivePanel());
47292                 continue;
47293             }
47294             if (!reg.panels.length) {
47295                 continue;
47296             }
47297             reg.showPanel(reg.getPanel(0));
47298         }
47299         
47300         
47301         
47302         
47303     },
47304     
47305     /**
47306      * Returns the nested BorderLayout for this panel
47307      * @return {Roo.BorderLayout} 
47308      */
47309     getLayout : function(){
47310         return this.layout;
47311     },
47312     
47313      /**
47314      * Adds a xtype elements to the layout of the nested panel
47315      * <pre><code>
47316
47317 panel.addxtype({
47318        xtype : 'ContentPanel',
47319        region: 'west',
47320        items: [ .... ]
47321    }
47322 );
47323
47324 panel.addxtype({
47325         xtype : 'NestedLayoutPanel',
47326         region: 'west',
47327         layout: {
47328            center: { },
47329            west: { }   
47330         },
47331         items : [ ... list of content panels or nested layout panels.. ]
47332    }
47333 );
47334 </code></pre>
47335      * @param {Object} cfg Xtype definition of item to add.
47336      */
47337     addxtype : function(cfg) {
47338         return this.layout.addxtype(cfg);
47339     
47340     }
47341 });
47342
47343 Roo.ScrollPanel = function(el, config, content){
47344     config = config || {};
47345     config.fitToFrame = true;
47346     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47347     
47348     this.el.dom.style.overflow = "hidden";
47349     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47350     this.el.removeClass("x-layout-inactive-content");
47351     this.el.on("mousewheel", this.onWheel, this);
47352
47353     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47354     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47355     up.unselectable(); down.unselectable();
47356     up.on("click", this.scrollUp, this);
47357     down.on("click", this.scrollDown, this);
47358     up.addClassOnOver("x-scroller-btn-over");
47359     down.addClassOnOver("x-scroller-btn-over");
47360     up.addClassOnClick("x-scroller-btn-click");
47361     down.addClassOnClick("x-scroller-btn-click");
47362     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47363
47364     this.resizeEl = this.el;
47365     this.el = wrap; this.up = up; this.down = down;
47366 };
47367
47368 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47369     increment : 100,
47370     wheelIncrement : 5,
47371     scrollUp : function(){
47372         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47373     },
47374
47375     scrollDown : function(){
47376         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47377     },
47378
47379     afterScroll : function(){
47380         var el = this.resizeEl;
47381         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47382         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47383         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47384     },
47385
47386     setSize : function(){
47387         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47388         this.afterScroll();
47389     },
47390
47391     onWheel : function(e){
47392         var d = e.getWheelDelta();
47393         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47394         this.afterScroll();
47395         e.stopEvent();
47396     },
47397
47398     setContent : function(content, loadScripts){
47399         this.resizeEl.update(content, loadScripts);
47400     }
47401
47402 });
47403
47404
47405
47406
47407
47408
47409
47410
47411
47412 /**
47413  * @class Roo.TreePanel
47414  * @extends Roo.ContentPanel
47415  * @constructor
47416  * Create a new TreePanel. - defaults to fit/scoll contents.
47417  * @param {String/Object} config A string to set only the panel's title, or a config object
47418  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47419  */
47420 Roo.TreePanel = function(config){
47421     var el = config.el;
47422     var tree = config.tree;
47423     delete config.tree; 
47424     delete config.el; // hopefull!
47425     
47426     // wrapper for IE7 strict & safari scroll issue
47427     
47428     var treeEl = el.createChild();
47429     config.resizeEl = treeEl;
47430     
47431     
47432     
47433     Roo.TreePanel.superclass.constructor.call(this, el, config);
47434  
47435  
47436     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47437     //console.log(tree);
47438     this.on('activate', function()
47439     {
47440         if (this.tree.rendered) {
47441             return;
47442         }
47443         //console.log('render tree');
47444         this.tree.render();
47445     });
47446     
47447     this.on('resize',  function (cp, w, h) {
47448             this.tree.innerCt.setWidth(w);
47449             this.tree.innerCt.setHeight(h);
47450             this.tree.innerCt.setStyle('overflow-y', 'auto');
47451     });
47452
47453         
47454     
47455 };
47456
47457 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47458     fitToFrame : true,
47459     autoScroll : true
47460 });
47461
47462
47463
47464
47465
47466
47467
47468
47469
47470
47471
47472 /*
47473  * Based on:
47474  * Ext JS Library 1.1.1
47475  * Copyright(c) 2006-2007, Ext JS, LLC.
47476  *
47477  * Originally Released Under LGPL - original licence link has changed is not relivant.
47478  *
47479  * Fork - LGPL
47480  * <script type="text/javascript">
47481  */
47482  
47483
47484 /**
47485  * @class Roo.ReaderLayout
47486  * @extends Roo.BorderLayout
47487  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47488  * center region containing two nested regions (a top one for a list view and one for item preview below),
47489  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47490  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47491  * expedites the setup of the overall layout and regions for this common application style.
47492  * Example:
47493  <pre><code>
47494 var reader = new Roo.ReaderLayout();
47495 var CP = Roo.ContentPanel;  // shortcut for adding
47496
47497 reader.beginUpdate();
47498 reader.add("north", new CP("north", "North"));
47499 reader.add("west", new CP("west", {title: "West"}));
47500 reader.add("east", new CP("east", {title: "East"}));
47501
47502 reader.regions.listView.add(new CP("listView", "List"));
47503 reader.regions.preview.add(new CP("preview", "Preview"));
47504 reader.endUpdate();
47505 </code></pre>
47506 * @constructor
47507 * Create a new ReaderLayout
47508 * @param {Object} config Configuration options
47509 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47510 * document.body if omitted)
47511 */
47512 Roo.ReaderLayout = function(config, renderTo){
47513     var c = config || {size:{}};
47514     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47515         north: c.north !== false ? Roo.apply({
47516             split:false,
47517             initialSize: 32,
47518             titlebar: false
47519         }, c.north) : false,
47520         west: c.west !== false ? Roo.apply({
47521             split:true,
47522             initialSize: 200,
47523             minSize: 175,
47524             maxSize: 400,
47525             titlebar: true,
47526             collapsible: true,
47527             animate: true,
47528             margins:{left:5,right:0,bottom:5,top:5},
47529             cmargins:{left:5,right:5,bottom:5,top:5}
47530         }, c.west) : false,
47531         east: c.east !== false ? Roo.apply({
47532             split:true,
47533             initialSize: 200,
47534             minSize: 175,
47535             maxSize: 400,
47536             titlebar: true,
47537             collapsible: true,
47538             animate: true,
47539             margins:{left:0,right:5,bottom:5,top:5},
47540             cmargins:{left:5,right:5,bottom:5,top:5}
47541         }, c.east) : false,
47542         center: Roo.apply({
47543             tabPosition: 'top',
47544             autoScroll:false,
47545             closeOnTab: true,
47546             titlebar:false,
47547             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47548         }, c.center)
47549     });
47550
47551     this.el.addClass('x-reader');
47552
47553     this.beginUpdate();
47554
47555     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47556         south: c.preview !== false ? Roo.apply({
47557             split:true,
47558             initialSize: 200,
47559             minSize: 100,
47560             autoScroll:true,
47561             collapsible:true,
47562             titlebar: true,
47563             cmargins:{top:5,left:0, right:0, bottom:0}
47564         }, c.preview) : false,
47565         center: Roo.apply({
47566             autoScroll:false,
47567             titlebar:false,
47568             minHeight:200
47569         }, c.listView)
47570     });
47571     this.add('center', new Roo.NestedLayoutPanel(inner,
47572             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47573
47574     this.endUpdate();
47575
47576     this.regions.preview = inner.getRegion('south');
47577     this.regions.listView = inner.getRegion('center');
47578 };
47579
47580 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47581  * Based on:
47582  * Ext JS Library 1.1.1
47583  * Copyright(c) 2006-2007, Ext JS, LLC.
47584  *
47585  * Originally Released Under LGPL - original licence link has changed is not relivant.
47586  *
47587  * Fork - LGPL
47588  * <script type="text/javascript">
47589  */
47590  
47591 /**
47592  * @class Roo.grid.Grid
47593  * @extends Roo.util.Observable
47594  * This class represents the primary interface of a component based grid control.
47595  * <br><br>Usage:<pre><code>
47596  var grid = new Roo.grid.Grid("my-container-id", {
47597      ds: myDataStore,
47598      cm: myColModel,
47599      selModel: mySelectionModel,
47600      autoSizeColumns: true,
47601      monitorWindowResize: false,
47602      trackMouseOver: true
47603  });
47604  // set any options
47605  grid.render();
47606  * </code></pre>
47607  * <b>Common Problems:</b><br/>
47608  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47609  * element will correct this<br/>
47610  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47611  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47612  * are unpredictable.<br/>
47613  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47614  * grid to calculate dimensions/offsets.<br/>
47615   * @constructor
47616  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47617  * The container MUST have some type of size defined for the grid to fill. The container will be
47618  * automatically set to position relative if it isn't already.
47619  * @param {Object} config A config object that sets properties on this grid.
47620  */
47621 Roo.grid.Grid = function(container, config){
47622         // initialize the container
47623         this.container = Roo.get(container);
47624         this.container.update("");
47625         this.container.setStyle("overflow", "hidden");
47626     this.container.addClass('x-grid-container');
47627
47628     this.id = this.container.id;
47629
47630     Roo.apply(this, config);
47631     // check and correct shorthanded configs
47632     if(this.ds){
47633         this.dataSource = this.ds;
47634         delete this.ds;
47635     }
47636     if(this.cm){
47637         this.colModel = this.cm;
47638         delete this.cm;
47639     }
47640     if(this.sm){
47641         this.selModel = this.sm;
47642         delete this.sm;
47643     }
47644
47645     if (this.selModel) {
47646         this.selModel = Roo.factory(this.selModel, Roo.grid);
47647         this.sm = this.selModel;
47648         this.sm.xmodule = this.xmodule || false;
47649     }
47650     if (typeof(this.colModel.config) == 'undefined') {
47651         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47652         this.cm = this.colModel;
47653         this.cm.xmodule = this.xmodule || false;
47654     }
47655     if (this.dataSource) {
47656         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47657         this.ds = this.dataSource;
47658         this.ds.xmodule = this.xmodule || false;
47659          
47660     }
47661     
47662     
47663     
47664     if(this.width){
47665         this.container.setWidth(this.width);
47666     }
47667
47668     if(this.height){
47669         this.container.setHeight(this.height);
47670     }
47671     /** @private */
47672         this.addEvents({
47673         // raw events
47674         /**
47675          * @event click
47676          * The raw click event for the entire grid.
47677          * @param {Roo.EventObject} e
47678          */
47679         "click" : true,
47680         /**
47681          * @event dblclick
47682          * The raw dblclick event for the entire grid.
47683          * @param {Roo.EventObject} e
47684          */
47685         "dblclick" : true,
47686         /**
47687          * @event contextmenu
47688          * The raw contextmenu event for the entire grid.
47689          * @param {Roo.EventObject} e
47690          */
47691         "contextmenu" : true,
47692         /**
47693          * @event mousedown
47694          * The raw mousedown event for the entire grid.
47695          * @param {Roo.EventObject} e
47696          */
47697         "mousedown" : true,
47698         /**
47699          * @event mouseup
47700          * The raw mouseup event for the entire grid.
47701          * @param {Roo.EventObject} e
47702          */
47703         "mouseup" : true,
47704         /**
47705          * @event mouseover
47706          * The raw mouseover event for the entire grid.
47707          * @param {Roo.EventObject} e
47708          */
47709         "mouseover" : true,
47710         /**
47711          * @event mouseout
47712          * The raw mouseout event for the entire grid.
47713          * @param {Roo.EventObject} e
47714          */
47715         "mouseout" : true,
47716         /**
47717          * @event keypress
47718          * The raw keypress event for the entire grid.
47719          * @param {Roo.EventObject} e
47720          */
47721         "keypress" : true,
47722         /**
47723          * @event keydown
47724          * The raw keydown event for the entire grid.
47725          * @param {Roo.EventObject} e
47726          */
47727         "keydown" : true,
47728
47729         // custom events
47730
47731         /**
47732          * @event cellclick
47733          * Fires when a cell is clicked
47734          * @param {Grid} this
47735          * @param {Number} rowIndex
47736          * @param {Number} columnIndex
47737          * @param {Roo.EventObject} e
47738          */
47739         "cellclick" : true,
47740         /**
47741          * @event celldblclick
47742          * Fires when a cell is double clicked
47743          * @param {Grid} this
47744          * @param {Number} rowIndex
47745          * @param {Number} columnIndex
47746          * @param {Roo.EventObject} e
47747          */
47748         "celldblclick" : true,
47749         /**
47750          * @event rowclick
47751          * Fires when a row is clicked
47752          * @param {Grid} this
47753          * @param {Number} rowIndex
47754          * @param {Roo.EventObject} e
47755          */
47756         "rowclick" : true,
47757         /**
47758          * @event rowdblclick
47759          * Fires when a row is double clicked
47760          * @param {Grid} this
47761          * @param {Number} rowIndex
47762          * @param {Roo.EventObject} e
47763          */
47764         "rowdblclick" : true,
47765         /**
47766          * @event headerclick
47767          * Fires when a header is clicked
47768          * @param {Grid} this
47769          * @param {Number} columnIndex
47770          * @param {Roo.EventObject} e
47771          */
47772         "headerclick" : true,
47773         /**
47774          * @event headerdblclick
47775          * Fires when a header cell is double clicked
47776          * @param {Grid} this
47777          * @param {Number} columnIndex
47778          * @param {Roo.EventObject} e
47779          */
47780         "headerdblclick" : true,
47781         /**
47782          * @event rowcontextmenu
47783          * Fires when a row is right clicked
47784          * @param {Grid} this
47785          * @param {Number} rowIndex
47786          * @param {Roo.EventObject} e
47787          */
47788         "rowcontextmenu" : true,
47789         /**
47790          * @event cellcontextmenu
47791          * Fires when a cell is right clicked
47792          * @param {Grid} this
47793          * @param {Number} rowIndex
47794          * @param {Number} cellIndex
47795          * @param {Roo.EventObject} e
47796          */
47797          "cellcontextmenu" : true,
47798         /**
47799          * @event headercontextmenu
47800          * Fires when a header is right clicked
47801          * @param {Grid} this
47802          * @param {Number} columnIndex
47803          * @param {Roo.EventObject} e
47804          */
47805         "headercontextmenu" : true,
47806         /**
47807          * @event bodyscroll
47808          * Fires when the body element is scrolled
47809          * @param {Number} scrollLeft
47810          * @param {Number} scrollTop
47811          */
47812         "bodyscroll" : true,
47813         /**
47814          * @event columnresize
47815          * Fires when the user resizes a column
47816          * @param {Number} columnIndex
47817          * @param {Number} newSize
47818          */
47819         "columnresize" : true,
47820         /**
47821          * @event columnmove
47822          * Fires when the user moves a column
47823          * @param {Number} oldIndex
47824          * @param {Number} newIndex
47825          */
47826         "columnmove" : true,
47827         /**
47828          * @event startdrag
47829          * Fires when row(s) start being dragged
47830          * @param {Grid} this
47831          * @param {Roo.GridDD} dd The drag drop object
47832          * @param {event} e The raw browser event
47833          */
47834         "startdrag" : true,
47835         /**
47836          * @event enddrag
47837          * Fires when a drag operation is complete
47838          * @param {Grid} this
47839          * @param {Roo.GridDD} dd The drag drop object
47840          * @param {event} e The raw browser event
47841          */
47842         "enddrag" : true,
47843         /**
47844          * @event dragdrop
47845          * Fires when dragged row(s) are dropped on a valid DD target
47846          * @param {Grid} this
47847          * @param {Roo.GridDD} dd The drag drop object
47848          * @param {String} targetId The target drag drop object
47849          * @param {event} e The raw browser event
47850          */
47851         "dragdrop" : true,
47852         /**
47853          * @event dragover
47854          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47855          * @param {Grid} this
47856          * @param {Roo.GridDD} dd The drag drop object
47857          * @param {String} targetId The target drag drop object
47858          * @param {event} e The raw browser event
47859          */
47860         "dragover" : true,
47861         /**
47862          * @event dragenter
47863          *  Fires when the dragged row(s) first cross another DD target while being dragged
47864          * @param {Grid} this
47865          * @param {Roo.GridDD} dd The drag drop object
47866          * @param {String} targetId The target drag drop object
47867          * @param {event} e The raw browser event
47868          */
47869         "dragenter" : true,
47870         /**
47871          * @event dragout
47872          * Fires when the dragged row(s) leave another DD target while being dragged
47873          * @param {Grid} this
47874          * @param {Roo.GridDD} dd The drag drop object
47875          * @param {String} targetId The target drag drop object
47876          * @param {event} e The raw browser event
47877          */
47878         "dragout" : true,
47879         /**
47880          * @event rowclass
47881          * Fires when a row is rendered, so you can change add a style to it.
47882          * @param {GridView} gridview   The grid view
47883          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47884          */
47885         'rowclass' : true,
47886
47887         /**
47888          * @event render
47889          * Fires when the grid is rendered
47890          * @param {Grid} grid
47891          */
47892         'render' : true
47893     });
47894
47895     Roo.grid.Grid.superclass.constructor.call(this);
47896 };
47897 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47898     
47899     /**
47900      * @cfg {String} ddGroup - drag drop group.
47901      */
47902
47903     /**
47904      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47905      */
47906     minColumnWidth : 25,
47907
47908     /**
47909      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47910      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47911      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47912      */
47913     autoSizeColumns : false,
47914
47915     /**
47916      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47917      */
47918     autoSizeHeaders : true,
47919
47920     /**
47921      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47922      */
47923     monitorWindowResize : true,
47924
47925     /**
47926      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47927      * rows measured to get a columns size. Default is 0 (all rows).
47928      */
47929     maxRowsToMeasure : 0,
47930
47931     /**
47932      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47933      */
47934     trackMouseOver : true,
47935
47936     /**
47937     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47938     */
47939     
47940     /**
47941     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47942     */
47943     enableDragDrop : false,
47944     
47945     /**
47946     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47947     */
47948     enableColumnMove : true,
47949     
47950     /**
47951     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47952     */
47953     enableColumnHide : true,
47954     
47955     /**
47956     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47957     */
47958     enableRowHeightSync : false,
47959     
47960     /**
47961     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47962     */
47963     stripeRows : true,
47964     
47965     /**
47966     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47967     */
47968     autoHeight : false,
47969
47970     /**
47971      * @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.
47972      */
47973     autoExpandColumn : false,
47974
47975     /**
47976     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47977     * Default is 50.
47978     */
47979     autoExpandMin : 50,
47980
47981     /**
47982     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47983     */
47984     autoExpandMax : 1000,
47985
47986     /**
47987     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47988     */
47989     view : null,
47990
47991     /**
47992     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47993     */
47994     loadMask : false,
47995     /**
47996     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47997     */
47998     dropTarget: false,
47999     
48000    
48001     
48002     // private
48003     rendered : false,
48004
48005     /**
48006     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
48007     * of a fixed width. Default is false.
48008     */
48009     /**
48010     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
48011     */
48012     /**
48013      * Called once after all setup has been completed and the grid is ready to be rendered.
48014      * @return {Roo.grid.Grid} this
48015      */
48016     render : function()
48017     {
48018         var c = this.container;
48019         // try to detect autoHeight/width mode
48020         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
48021             this.autoHeight = true;
48022         }
48023         var view = this.getView();
48024         view.init(this);
48025
48026         c.on("click", this.onClick, this);
48027         c.on("dblclick", this.onDblClick, this);
48028         c.on("contextmenu", this.onContextMenu, this);
48029         c.on("keydown", this.onKeyDown, this);
48030
48031         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
48032
48033         this.getSelectionModel().init(this);
48034
48035         view.render();
48036
48037         if(this.loadMask){
48038             this.loadMask = new Roo.LoadMask(this.container,
48039                     Roo.apply({store:this.dataSource}, this.loadMask));
48040         }
48041         
48042         
48043         if (this.toolbar && this.toolbar.xtype) {
48044             this.toolbar.container = this.getView().getHeaderPanel(true);
48045             this.toolbar = new Roo.Toolbar(this.toolbar);
48046         }
48047         if (this.footer && this.footer.xtype) {
48048             this.footer.dataSource = this.getDataSource();
48049             this.footer.container = this.getView().getFooterPanel(true);
48050             this.footer = Roo.factory(this.footer, Roo);
48051         }
48052         if (this.dropTarget && this.dropTarget.xtype) {
48053             delete this.dropTarget.xtype;
48054             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
48055         }
48056         
48057         
48058         this.rendered = true;
48059         this.fireEvent('render', this);
48060         return this;
48061     },
48062
48063         /**
48064          * Reconfigures the grid to use a different Store and Column Model.
48065          * The View will be bound to the new objects and refreshed.
48066          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
48067          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
48068          */
48069     reconfigure : function(dataSource, colModel){
48070         if(this.loadMask){
48071             this.loadMask.destroy();
48072             this.loadMask = new Roo.LoadMask(this.container,
48073                     Roo.apply({store:dataSource}, this.loadMask));
48074         }
48075         this.view.bind(dataSource, colModel);
48076         this.dataSource = dataSource;
48077         this.colModel = colModel;
48078         this.view.refresh(true);
48079     },
48080
48081     // private
48082     onKeyDown : function(e){
48083         this.fireEvent("keydown", e);
48084     },
48085
48086     /**
48087      * Destroy this grid.
48088      * @param {Boolean} removeEl True to remove the element
48089      */
48090     destroy : function(removeEl, keepListeners){
48091         if(this.loadMask){
48092             this.loadMask.destroy();
48093         }
48094         var c = this.container;
48095         c.removeAllListeners();
48096         this.view.destroy();
48097         this.colModel.purgeListeners();
48098         if(!keepListeners){
48099             this.purgeListeners();
48100         }
48101         c.update("");
48102         if(removeEl === true){
48103             c.remove();
48104         }
48105     },
48106
48107     // private
48108     processEvent : function(name, e){
48109         this.fireEvent(name, e);
48110         var t = e.getTarget();
48111         var v = this.view;
48112         var header = v.findHeaderIndex(t);
48113         if(header !== false){
48114             this.fireEvent("header" + name, this, header, e);
48115         }else{
48116             var row = v.findRowIndex(t);
48117             var cell = v.findCellIndex(t);
48118             if(row !== false){
48119                 this.fireEvent("row" + name, this, row, e);
48120                 if(cell !== false){
48121                     this.fireEvent("cell" + name, this, row, cell, e);
48122                 }
48123             }
48124         }
48125     },
48126
48127     // private
48128     onClick : function(e){
48129         this.processEvent("click", e);
48130     },
48131
48132     // private
48133     onContextMenu : function(e, t){
48134         this.processEvent("contextmenu", e);
48135     },
48136
48137     // private
48138     onDblClick : function(e){
48139         this.processEvent("dblclick", e);
48140     },
48141
48142     // private
48143     walkCells : function(row, col, step, fn, scope){
48144         var cm = this.colModel, clen = cm.getColumnCount();
48145         var ds = this.dataSource, rlen = ds.getCount(), first = true;
48146         if(step < 0){
48147             if(col < 0){
48148                 row--;
48149                 first = false;
48150             }
48151             while(row >= 0){
48152                 if(!first){
48153                     col = clen-1;
48154                 }
48155                 first = false;
48156                 while(col >= 0){
48157                     if(fn.call(scope || this, row, col, cm) === true){
48158                         return [row, col];
48159                     }
48160                     col--;
48161                 }
48162                 row--;
48163             }
48164         } else {
48165             if(col >= clen){
48166                 row++;
48167                 first = false;
48168             }
48169             while(row < rlen){
48170                 if(!first){
48171                     col = 0;
48172                 }
48173                 first = false;
48174                 while(col < clen){
48175                     if(fn.call(scope || this, row, col, cm) === true){
48176                         return [row, col];
48177                     }
48178                     col++;
48179                 }
48180                 row++;
48181             }
48182         }
48183         return null;
48184     },
48185
48186     // private
48187     getSelections : function(){
48188         return this.selModel.getSelections();
48189     },
48190
48191     /**
48192      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48193      * but if manual update is required this method will initiate it.
48194      */
48195     autoSize : function(){
48196         if(this.rendered){
48197             this.view.layout();
48198             if(this.view.adjustForScroll){
48199                 this.view.adjustForScroll();
48200             }
48201         }
48202     },
48203
48204     /**
48205      * Returns the grid's underlying element.
48206      * @return {Element} The element
48207      */
48208     getGridEl : function(){
48209         return this.container;
48210     },
48211
48212     // private for compatibility, overridden by editor grid
48213     stopEditing : function(){},
48214
48215     /**
48216      * Returns the grid's SelectionModel.
48217      * @return {SelectionModel}
48218      */
48219     getSelectionModel : function(){
48220         if(!this.selModel){
48221             this.selModel = new Roo.grid.RowSelectionModel();
48222         }
48223         return this.selModel;
48224     },
48225
48226     /**
48227      * Returns the grid's DataSource.
48228      * @return {DataSource}
48229      */
48230     getDataSource : function(){
48231         return this.dataSource;
48232     },
48233
48234     /**
48235      * Returns the grid's ColumnModel.
48236      * @return {ColumnModel}
48237      */
48238     getColumnModel : function(){
48239         return this.colModel;
48240     },
48241
48242     /**
48243      * Returns the grid's GridView object.
48244      * @return {GridView}
48245      */
48246     getView : function(){
48247         if(!this.view){
48248             this.view = new Roo.grid.GridView(this.viewConfig);
48249         }
48250         return this.view;
48251     },
48252     /**
48253      * Called to get grid's drag proxy text, by default returns this.ddText.
48254      * @return {String}
48255      */
48256     getDragDropText : function(){
48257         var count = this.selModel.getCount();
48258         return String.format(this.ddText, count, count == 1 ? '' : 's');
48259     }
48260 });
48261 /**
48262  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48263  * %0 is replaced with the number of selected rows.
48264  * @type String
48265  */
48266 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48267  * Based on:
48268  * Ext JS Library 1.1.1
48269  * Copyright(c) 2006-2007, Ext JS, LLC.
48270  *
48271  * Originally Released Under LGPL - original licence link has changed is not relivant.
48272  *
48273  * Fork - LGPL
48274  * <script type="text/javascript">
48275  */
48276  
48277 Roo.grid.AbstractGridView = function(){
48278         this.grid = null;
48279         
48280         this.events = {
48281             "beforerowremoved" : true,
48282             "beforerowsinserted" : true,
48283             "beforerefresh" : true,
48284             "rowremoved" : true,
48285             "rowsinserted" : true,
48286             "rowupdated" : true,
48287             "refresh" : true
48288         };
48289     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48290 };
48291
48292 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48293     rowClass : "x-grid-row",
48294     cellClass : "x-grid-cell",
48295     tdClass : "x-grid-td",
48296     hdClass : "x-grid-hd",
48297     splitClass : "x-grid-hd-split",
48298     
48299         init: function(grid){
48300         this.grid = grid;
48301                 var cid = this.grid.getGridEl().id;
48302         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48303         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48304         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48305         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48306         },
48307         
48308         getColumnRenderers : function(){
48309         var renderers = [];
48310         var cm = this.grid.colModel;
48311         var colCount = cm.getColumnCount();
48312         for(var i = 0; i < colCount; i++){
48313             renderers[i] = cm.getRenderer(i);
48314         }
48315         return renderers;
48316     },
48317     
48318     getColumnIds : function(){
48319         var ids = [];
48320         var cm = this.grid.colModel;
48321         var colCount = cm.getColumnCount();
48322         for(var i = 0; i < colCount; i++){
48323             ids[i] = cm.getColumnId(i);
48324         }
48325         return ids;
48326     },
48327     
48328     getDataIndexes : function(){
48329         if(!this.indexMap){
48330             this.indexMap = this.buildIndexMap();
48331         }
48332         return this.indexMap.colToData;
48333     },
48334     
48335     getColumnIndexByDataIndex : function(dataIndex){
48336         if(!this.indexMap){
48337             this.indexMap = this.buildIndexMap();
48338         }
48339         return this.indexMap.dataToCol[dataIndex];
48340     },
48341     
48342     /**
48343      * Set a css style for a column dynamically. 
48344      * @param {Number} colIndex The index of the column
48345      * @param {String} name The css property name
48346      * @param {String} value The css value
48347      */
48348     setCSSStyle : function(colIndex, name, value){
48349         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48350         Roo.util.CSS.updateRule(selector, name, value);
48351     },
48352     
48353     generateRules : function(cm){
48354         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48355         Roo.util.CSS.removeStyleSheet(rulesId);
48356         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48357             var cid = cm.getColumnId(i);
48358             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48359                          this.tdSelector, cid, " {\n}\n",
48360                          this.hdSelector, cid, " {\n}\n",
48361                          this.splitSelector, cid, " {\n}\n");
48362         }
48363         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48364     }
48365 });/*
48366  * Based on:
48367  * Ext JS Library 1.1.1
48368  * Copyright(c) 2006-2007, Ext JS, LLC.
48369  *
48370  * Originally Released Under LGPL - original licence link has changed is not relivant.
48371  *
48372  * Fork - LGPL
48373  * <script type="text/javascript">
48374  */
48375
48376 // private
48377 // This is a support class used internally by the Grid components
48378 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48379     this.grid = grid;
48380     this.view = grid.getView();
48381     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48382     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48383     if(hd2){
48384         this.setHandleElId(Roo.id(hd));
48385         this.setOuterHandleElId(Roo.id(hd2));
48386     }
48387     this.scroll = false;
48388 };
48389 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48390     maxDragWidth: 120,
48391     getDragData : function(e){
48392         var t = Roo.lib.Event.getTarget(e);
48393         var h = this.view.findHeaderCell(t);
48394         if(h){
48395             return {ddel: h.firstChild, header:h};
48396         }
48397         return false;
48398     },
48399
48400     onInitDrag : function(e){
48401         this.view.headersDisabled = true;
48402         var clone = this.dragData.ddel.cloneNode(true);
48403         clone.id = Roo.id();
48404         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48405         this.proxy.update(clone);
48406         return true;
48407     },
48408
48409     afterValidDrop : function(){
48410         var v = this.view;
48411         setTimeout(function(){
48412             v.headersDisabled = false;
48413         }, 50);
48414     },
48415
48416     afterInvalidDrop : function(){
48417         var v = this.view;
48418         setTimeout(function(){
48419             v.headersDisabled = false;
48420         }, 50);
48421     }
48422 });
48423 /*
48424  * Based on:
48425  * Ext JS Library 1.1.1
48426  * Copyright(c) 2006-2007, Ext JS, LLC.
48427  *
48428  * Originally Released Under LGPL - original licence link has changed is not relivant.
48429  *
48430  * Fork - LGPL
48431  * <script type="text/javascript">
48432  */
48433 // private
48434 // This is a support class used internally by the Grid components
48435 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48436     this.grid = grid;
48437     this.view = grid.getView();
48438     // split the proxies so they don't interfere with mouse events
48439     this.proxyTop = Roo.DomHelper.append(document.body, {
48440         cls:"col-move-top", html:"&#160;"
48441     }, true);
48442     this.proxyBottom = Roo.DomHelper.append(document.body, {
48443         cls:"col-move-bottom", html:"&#160;"
48444     }, true);
48445     this.proxyTop.hide = this.proxyBottom.hide = function(){
48446         this.setLeftTop(-100,-100);
48447         this.setStyle("visibility", "hidden");
48448     };
48449     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48450     // temporarily disabled
48451     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48452     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48453 };
48454 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48455     proxyOffsets : [-4, -9],
48456     fly: Roo.Element.fly,
48457
48458     getTargetFromEvent : function(e){
48459         var t = Roo.lib.Event.getTarget(e);
48460         var cindex = this.view.findCellIndex(t);
48461         if(cindex !== false){
48462             return this.view.getHeaderCell(cindex);
48463         }
48464         return null;
48465     },
48466
48467     nextVisible : function(h){
48468         var v = this.view, cm = this.grid.colModel;
48469         h = h.nextSibling;
48470         while(h){
48471             if(!cm.isHidden(v.getCellIndex(h))){
48472                 return h;
48473             }
48474             h = h.nextSibling;
48475         }
48476         return null;
48477     },
48478
48479     prevVisible : function(h){
48480         var v = this.view, cm = this.grid.colModel;
48481         h = h.prevSibling;
48482         while(h){
48483             if(!cm.isHidden(v.getCellIndex(h))){
48484                 return h;
48485             }
48486             h = h.prevSibling;
48487         }
48488         return null;
48489     },
48490
48491     positionIndicator : function(h, n, e){
48492         var x = Roo.lib.Event.getPageX(e);
48493         var r = Roo.lib.Dom.getRegion(n.firstChild);
48494         var px, pt, py = r.top + this.proxyOffsets[1];
48495         if((r.right - x) <= (r.right-r.left)/2){
48496             px = r.right+this.view.borderWidth;
48497             pt = "after";
48498         }else{
48499             px = r.left;
48500             pt = "before";
48501         }
48502         var oldIndex = this.view.getCellIndex(h);
48503         var newIndex = this.view.getCellIndex(n);
48504
48505         if(this.grid.colModel.isFixed(newIndex)){
48506             return false;
48507         }
48508
48509         var locked = this.grid.colModel.isLocked(newIndex);
48510
48511         if(pt == "after"){
48512             newIndex++;
48513         }
48514         if(oldIndex < newIndex){
48515             newIndex--;
48516         }
48517         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48518             return false;
48519         }
48520         px +=  this.proxyOffsets[0];
48521         this.proxyTop.setLeftTop(px, py);
48522         this.proxyTop.show();
48523         if(!this.bottomOffset){
48524             this.bottomOffset = this.view.mainHd.getHeight();
48525         }
48526         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48527         this.proxyBottom.show();
48528         return pt;
48529     },
48530
48531     onNodeEnter : function(n, dd, e, data){
48532         if(data.header != n){
48533             this.positionIndicator(data.header, n, e);
48534         }
48535     },
48536
48537     onNodeOver : function(n, dd, e, data){
48538         var result = false;
48539         if(data.header != n){
48540             result = this.positionIndicator(data.header, n, e);
48541         }
48542         if(!result){
48543             this.proxyTop.hide();
48544             this.proxyBottom.hide();
48545         }
48546         return result ? this.dropAllowed : this.dropNotAllowed;
48547     },
48548
48549     onNodeOut : function(n, dd, e, data){
48550         this.proxyTop.hide();
48551         this.proxyBottom.hide();
48552     },
48553
48554     onNodeDrop : function(n, dd, e, data){
48555         var h = data.header;
48556         if(h != n){
48557             var cm = this.grid.colModel;
48558             var x = Roo.lib.Event.getPageX(e);
48559             var r = Roo.lib.Dom.getRegion(n.firstChild);
48560             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48561             var oldIndex = this.view.getCellIndex(h);
48562             var newIndex = this.view.getCellIndex(n);
48563             var locked = cm.isLocked(newIndex);
48564             if(pt == "after"){
48565                 newIndex++;
48566             }
48567             if(oldIndex < newIndex){
48568                 newIndex--;
48569             }
48570             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48571                 return false;
48572             }
48573             cm.setLocked(oldIndex, locked, true);
48574             cm.moveColumn(oldIndex, newIndex);
48575             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48576             return true;
48577         }
48578         return false;
48579     }
48580 });
48581 /*
48582  * Based on:
48583  * Ext JS Library 1.1.1
48584  * Copyright(c) 2006-2007, Ext JS, LLC.
48585  *
48586  * Originally Released Under LGPL - original licence link has changed is not relivant.
48587  *
48588  * Fork - LGPL
48589  * <script type="text/javascript">
48590  */
48591   
48592 /**
48593  * @class Roo.grid.GridView
48594  * @extends Roo.util.Observable
48595  *
48596  * @constructor
48597  * @param {Object} config
48598  */
48599 Roo.grid.GridView = function(config){
48600     Roo.grid.GridView.superclass.constructor.call(this);
48601     this.el = null;
48602
48603     Roo.apply(this, config);
48604 };
48605
48606 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48607
48608     
48609     rowClass : "x-grid-row",
48610
48611     cellClass : "x-grid-col",
48612
48613     tdClass : "x-grid-td",
48614
48615     hdClass : "x-grid-hd",
48616
48617     splitClass : "x-grid-split",
48618
48619     sortClasses : ["sort-asc", "sort-desc"],
48620
48621     enableMoveAnim : false,
48622
48623     hlColor: "C3DAF9",
48624
48625     dh : Roo.DomHelper,
48626
48627     fly : Roo.Element.fly,
48628
48629     css : Roo.util.CSS,
48630
48631     borderWidth: 1,
48632
48633     splitOffset: 3,
48634
48635     scrollIncrement : 22,
48636
48637     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48638
48639     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48640
48641     bind : function(ds, cm){
48642         if(this.ds){
48643             this.ds.un("load", this.onLoad, this);
48644             this.ds.un("datachanged", this.onDataChange, this);
48645             this.ds.un("add", this.onAdd, this);
48646             this.ds.un("remove", this.onRemove, this);
48647             this.ds.un("update", this.onUpdate, this);
48648             this.ds.un("clear", this.onClear, this);
48649         }
48650         if(ds){
48651             ds.on("load", this.onLoad, this);
48652             ds.on("datachanged", this.onDataChange, this);
48653             ds.on("add", this.onAdd, this);
48654             ds.on("remove", this.onRemove, this);
48655             ds.on("update", this.onUpdate, this);
48656             ds.on("clear", this.onClear, this);
48657         }
48658         this.ds = ds;
48659
48660         if(this.cm){
48661             this.cm.un("widthchange", this.onColWidthChange, this);
48662             this.cm.un("headerchange", this.onHeaderChange, this);
48663             this.cm.un("hiddenchange", this.onHiddenChange, this);
48664             this.cm.un("columnmoved", this.onColumnMove, this);
48665             this.cm.un("columnlockchange", this.onColumnLock, this);
48666         }
48667         if(cm){
48668             this.generateRules(cm);
48669             cm.on("widthchange", this.onColWidthChange, this);
48670             cm.on("headerchange", this.onHeaderChange, this);
48671             cm.on("hiddenchange", this.onHiddenChange, this);
48672             cm.on("columnmoved", this.onColumnMove, this);
48673             cm.on("columnlockchange", this.onColumnLock, this);
48674         }
48675         this.cm = cm;
48676     },
48677
48678     init: function(grid){
48679         Roo.grid.GridView.superclass.init.call(this, grid);
48680
48681         this.bind(grid.dataSource, grid.colModel);
48682
48683         grid.on("headerclick", this.handleHeaderClick, this);
48684
48685         if(grid.trackMouseOver){
48686             grid.on("mouseover", this.onRowOver, this);
48687             grid.on("mouseout", this.onRowOut, this);
48688         }
48689         grid.cancelTextSelection = function(){};
48690         this.gridId = grid.id;
48691
48692         var tpls = this.templates || {};
48693
48694         if(!tpls.master){
48695             tpls.master = new Roo.Template(
48696                '<div class="x-grid" hidefocus="true">',
48697                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48698                   '<div class="x-grid-topbar"></div>',
48699                   '<div class="x-grid-scroller"><div></div></div>',
48700                   '<div class="x-grid-locked">',
48701                       '<div class="x-grid-header">{lockedHeader}</div>',
48702                       '<div class="x-grid-body">{lockedBody}</div>',
48703                   "</div>",
48704                   '<div class="x-grid-viewport">',
48705                       '<div class="x-grid-header">{header}</div>',
48706                       '<div class="x-grid-body">{body}</div>',
48707                   "</div>",
48708                   '<div class="x-grid-bottombar"></div>',
48709                  
48710                   '<div class="x-grid-resize-proxy">&#160;</div>',
48711                "</div>"
48712             );
48713             tpls.master.disableformats = true;
48714         }
48715
48716         if(!tpls.header){
48717             tpls.header = new Roo.Template(
48718                '<table border="0" cellspacing="0" cellpadding="0">',
48719                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48720                "</table>{splits}"
48721             );
48722             tpls.header.disableformats = true;
48723         }
48724         tpls.header.compile();
48725
48726         if(!tpls.hcell){
48727             tpls.hcell = new Roo.Template(
48728                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48729                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48730                 "</div></td>"
48731              );
48732              tpls.hcell.disableFormats = true;
48733         }
48734         tpls.hcell.compile();
48735
48736         if(!tpls.hsplit){
48737             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48738             tpls.hsplit.disableFormats = true;
48739         }
48740         tpls.hsplit.compile();
48741
48742         if(!tpls.body){
48743             tpls.body = new Roo.Template(
48744                '<table border="0" cellspacing="0" cellpadding="0">',
48745                "<tbody>{rows}</tbody>",
48746                "</table>"
48747             );
48748             tpls.body.disableFormats = true;
48749         }
48750         tpls.body.compile();
48751
48752         if(!tpls.row){
48753             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48754             tpls.row.disableFormats = true;
48755         }
48756         tpls.row.compile();
48757
48758         if(!tpls.cell){
48759             tpls.cell = new Roo.Template(
48760                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48761                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48762                 "</td>"
48763             );
48764             tpls.cell.disableFormats = true;
48765         }
48766         tpls.cell.compile();
48767
48768         this.templates = tpls;
48769     },
48770
48771     // remap these for backwards compat
48772     onColWidthChange : function(){
48773         this.updateColumns.apply(this, arguments);
48774     },
48775     onHeaderChange : function(){
48776         this.updateHeaders.apply(this, arguments);
48777     }, 
48778     onHiddenChange : function(){
48779         this.handleHiddenChange.apply(this, arguments);
48780     },
48781     onColumnMove : function(){
48782         this.handleColumnMove.apply(this, arguments);
48783     },
48784     onColumnLock : function(){
48785         this.handleLockChange.apply(this, arguments);
48786     },
48787
48788     onDataChange : function(){
48789         this.refresh();
48790         this.updateHeaderSortState();
48791     },
48792
48793     onClear : function(){
48794         this.refresh();
48795     },
48796
48797     onUpdate : function(ds, record){
48798         this.refreshRow(record);
48799     },
48800
48801     refreshRow : function(record){
48802         var ds = this.ds, index;
48803         if(typeof record == 'number'){
48804             index = record;
48805             record = ds.getAt(index);
48806         }else{
48807             index = ds.indexOf(record);
48808         }
48809         this.insertRows(ds, index, index, true);
48810         this.onRemove(ds, record, index+1, true);
48811         this.syncRowHeights(index, index);
48812         this.layout();
48813         this.fireEvent("rowupdated", this, index, record);
48814     },
48815
48816     onAdd : function(ds, records, index){
48817         this.insertRows(ds, index, index + (records.length-1));
48818     },
48819
48820     onRemove : function(ds, record, index, isUpdate){
48821         if(isUpdate !== true){
48822             this.fireEvent("beforerowremoved", this, index, record);
48823         }
48824         var bt = this.getBodyTable(), lt = this.getLockedTable();
48825         if(bt.rows[index]){
48826             bt.firstChild.removeChild(bt.rows[index]);
48827         }
48828         if(lt.rows[index]){
48829             lt.firstChild.removeChild(lt.rows[index]);
48830         }
48831         if(isUpdate !== true){
48832             this.stripeRows(index);
48833             this.syncRowHeights(index, index);
48834             this.layout();
48835             this.fireEvent("rowremoved", this, index, record);
48836         }
48837     },
48838
48839     onLoad : function(){
48840         this.scrollToTop();
48841     },
48842
48843     /**
48844      * Scrolls the grid to the top
48845      */
48846     scrollToTop : function(){
48847         if(this.scroller){
48848             this.scroller.dom.scrollTop = 0;
48849             this.syncScroll();
48850         }
48851     },
48852
48853     /**
48854      * Gets a panel in the header of the grid that can be used for toolbars etc.
48855      * After modifying the contents of this panel a call to grid.autoSize() may be
48856      * required to register any changes in size.
48857      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48858      * @return Roo.Element
48859      */
48860     getHeaderPanel : function(doShow){
48861         if(doShow){
48862             this.headerPanel.show();
48863         }
48864         return this.headerPanel;
48865     },
48866
48867     /**
48868      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48869      * After modifying the contents of this panel a call to grid.autoSize() may be
48870      * required to register any changes in size.
48871      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48872      * @return Roo.Element
48873      */
48874     getFooterPanel : function(doShow){
48875         if(doShow){
48876             this.footerPanel.show();
48877         }
48878         return this.footerPanel;
48879     },
48880
48881     initElements : function(){
48882         var E = Roo.Element;
48883         var el = this.grid.getGridEl().dom.firstChild;
48884         var cs = el.childNodes;
48885
48886         this.el = new E(el);
48887         
48888          this.focusEl = new E(el.firstChild);
48889         this.focusEl.swallowEvent("click", true);
48890         
48891         this.headerPanel = new E(cs[1]);
48892         this.headerPanel.enableDisplayMode("block");
48893
48894         this.scroller = new E(cs[2]);
48895         this.scrollSizer = new E(this.scroller.dom.firstChild);
48896
48897         this.lockedWrap = new E(cs[3]);
48898         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48899         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48900
48901         this.mainWrap = new E(cs[4]);
48902         this.mainHd = new E(this.mainWrap.dom.firstChild);
48903         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48904
48905         this.footerPanel = new E(cs[5]);
48906         this.footerPanel.enableDisplayMode("block");
48907
48908         this.resizeProxy = new E(cs[6]);
48909
48910         this.headerSelector = String.format(
48911            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48912            this.lockedHd.id, this.mainHd.id
48913         );
48914
48915         this.splitterSelector = String.format(
48916            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48917            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48918         );
48919     },
48920     idToCssName : function(s)
48921     {
48922         return s.replace(/[^a-z0-9]+/ig, '-');
48923     },
48924
48925     getHeaderCell : function(index){
48926         return Roo.DomQuery.select(this.headerSelector)[index];
48927     },
48928
48929     getHeaderCellMeasure : function(index){
48930         return this.getHeaderCell(index).firstChild;
48931     },
48932
48933     getHeaderCellText : function(index){
48934         return this.getHeaderCell(index).firstChild.firstChild;
48935     },
48936
48937     getLockedTable : function(){
48938         return this.lockedBody.dom.firstChild;
48939     },
48940
48941     getBodyTable : function(){
48942         return this.mainBody.dom.firstChild;
48943     },
48944
48945     getLockedRow : function(index){
48946         return this.getLockedTable().rows[index];
48947     },
48948
48949     getRow : function(index){
48950         return this.getBodyTable().rows[index];
48951     },
48952
48953     getRowComposite : function(index){
48954         if(!this.rowEl){
48955             this.rowEl = new Roo.CompositeElementLite();
48956         }
48957         var els = [], lrow, mrow;
48958         if(lrow = this.getLockedRow(index)){
48959             els.push(lrow);
48960         }
48961         if(mrow = this.getRow(index)){
48962             els.push(mrow);
48963         }
48964         this.rowEl.elements = els;
48965         return this.rowEl;
48966     },
48967     /**
48968      * Gets the 'td' of the cell
48969      * 
48970      * @param {Integer} rowIndex row to select
48971      * @param {Integer} colIndex column to select
48972      * 
48973      * @return {Object} 
48974      */
48975     getCell : function(rowIndex, colIndex){
48976         var locked = this.cm.getLockedCount();
48977         var source;
48978         if(colIndex < locked){
48979             source = this.lockedBody.dom.firstChild;
48980         }else{
48981             source = this.mainBody.dom.firstChild;
48982             colIndex -= locked;
48983         }
48984         return source.rows[rowIndex].childNodes[colIndex];
48985     },
48986
48987     getCellText : function(rowIndex, colIndex){
48988         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48989     },
48990
48991     getCellBox : function(cell){
48992         var b = this.fly(cell).getBox();
48993         if(Roo.isOpera){ // opera fails to report the Y
48994             b.y = cell.offsetTop + this.mainBody.getY();
48995         }
48996         return b;
48997     },
48998
48999     getCellIndex : function(cell){
49000         var id = String(cell.className).match(this.cellRE);
49001         if(id){
49002             return parseInt(id[1], 10);
49003         }
49004         return 0;
49005     },
49006
49007     findHeaderIndex : function(n){
49008         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49009         return r ? this.getCellIndex(r) : false;
49010     },
49011
49012     findHeaderCell : function(n){
49013         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49014         return r ? r : false;
49015     },
49016
49017     findRowIndex : function(n){
49018         if(!n){
49019             return false;
49020         }
49021         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
49022         return r ? r.rowIndex : false;
49023     },
49024
49025     findCellIndex : function(node){
49026         var stop = this.el.dom;
49027         while(node && node != stop){
49028             if(this.findRE.test(node.className)){
49029                 return this.getCellIndex(node);
49030             }
49031             node = node.parentNode;
49032         }
49033         return false;
49034     },
49035
49036     getColumnId : function(index){
49037         return this.cm.getColumnId(index);
49038     },
49039
49040     getSplitters : function()
49041     {
49042         if(this.splitterSelector){
49043            return Roo.DomQuery.select(this.splitterSelector);
49044         }else{
49045             return null;
49046       }
49047     },
49048
49049     getSplitter : function(index){
49050         return this.getSplitters()[index];
49051     },
49052
49053     onRowOver : function(e, t){
49054         var row;
49055         if((row = this.findRowIndex(t)) !== false){
49056             this.getRowComposite(row).addClass("x-grid-row-over");
49057         }
49058     },
49059
49060     onRowOut : function(e, t){
49061         var row;
49062         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
49063             this.getRowComposite(row).removeClass("x-grid-row-over");
49064         }
49065     },
49066
49067     renderHeaders : function(){
49068         var cm = this.cm;
49069         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
49070         var cb = [], lb = [], sb = [], lsb = [], p = {};
49071         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49072             p.cellId = "x-grid-hd-0-" + i;
49073             p.splitId = "x-grid-csplit-0-" + i;
49074             p.id = cm.getColumnId(i);
49075             p.title = cm.getColumnTooltip(i) || "";
49076             p.value = cm.getColumnHeader(i) || "";
49077             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
49078             if(!cm.isLocked(i)){
49079                 cb[cb.length] = ct.apply(p);
49080                 sb[sb.length] = st.apply(p);
49081             }else{
49082                 lb[lb.length] = ct.apply(p);
49083                 lsb[lsb.length] = st.apply(p);
49084             }
49085         }
49086         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
49087                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
49088     },
49089
49090     updateHeaders : function(){
49091         var html = this.renderHeaders();
49092         this.lockedHd.update(html[0]);
49093         this.mainHd.update(html[1]);
49094     },
49095
49096     /**
49097      * Focuses the specified row.
49098      * @param {Number} row The row index
49099      */
49100     focusRow : function(row)
49101     {
49102         //Roo.log('GridView.focusRow');
49103         var x = this.scroller.dom.scrollLeft;
49104         this.focusCell(row, 0, false);
49105         this.scroller.dom.scrollLeft = x;
49106     },
49107
49108     /**
49109      * Focuses the specified cell.
49110      * @param {Number} row The row index
49111      * @param {Number} col The column index
49112      * @param {Boolean} hscroll false to disable horizontal scrolling
49113      */
49114     focusCell : function(row, col, hscroll)
49115     {
49116         //Roo.log('GridView.focusCell');
49117         var el = this.ensureVisible(row, col, hscroll);
49118         this.focusEl.alignTo(el, "tl-tl");
49119         if(Roo.isGecko){
49120             this.focusEl.focus();
49121         }else{
49122             this.focusEl.focus.defer(1, this.focusEl);
49123         }
49124     },
49125
49126     /**
49127      * Scrolls the specified cell into view
49128      * @param {Number} row The row index
49129      * @param {Number} col The column index
49130      * @param {Boolean} hscroll false to disable horizontal scrolling
49131      */
49132     ensureVisible : function(row, col, hscroll)
49133     {
49134         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
49135         //return null; //disable for testing.
49136         if(typeof row != "number"){
49137             row = row.rowIndex;
49138         }
49139         if(row < 0 && row >= this.ds.getCount()){
49140             return  null;
49141         }
49142         col = (col !== undefined ? col : 0);
49143         var cm = this.grid.colModel;
49144         while(cm.isHidden(col)){
49145             col++;
49146         }
49147
49148         var el = this.getCell(row, col);
49149         if(!el){
49150             return null;
49151         }
49152         var c = this.scroller.dom;
49153
49154         var ctop = parseInt(el.offsetTop, 10);
49155         var cleft = parseInt(el.offsetLeft, 10);
49156         var cbot = ctop + el.offsetHeight;
49157         var cright = cleft + el.offsetWidth;
49158         
49159         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
49160         var stop = parseInt(c.scrollTop, 10);
49161         var sleft = parseInt(c.scrollLeft, 10);
49162         var sbot = stop + ch;
49163         var sright = sleft + c.clientWidth;
49164         /*
49165         Roo.log('GridView.ensureVisible:' +
49166                 ' ctop:' + ctop +
49167                 ' c.clientHeight:' + c.clientHeight +
49168                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
49169                 ' stop:' + stop +
49170                 ' cbot:' + cbot +
49171                 ' sbot:' + sbot +
49172                 ' ch:' + ch  
49173                 );
49174         */
49175         if(ctop < stop){
49176              c.scrollTop = ctop;
49177             //Roo.log("set scrolltop to ctop DISABLE?");
49178         }else if(cbot > sbot){
49179             //Roo.log("set scrolltop to cbot-ch");
49180             c.scrollTop = cbot-ch;
49181         }
49182         
49183         if(hscroll !== false){
49184             if(cleft < sleft){
49185                 c.scrollLeft = cleft;
49186             }else if(cright > sright){
49187                 c.scrollLeft = cright-c.clientWidth;
49188             }
49189         }
49190          
49191         return el;
49192     },
49193
49194     updateColumns : function(){
49195         this.grid.stopEditing();
49196         var cm = this.grid.colModel, colIds = this.getColumnIds();
49197         //var totalWidth = cm.getTotalWidth();
49198         var pos = 0;
49199         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49200             //if(cm.isHidden(i)) continue;
49201             var w = cm.getColumnWidth(i);
49202             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49203             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49204         }
49205         this.updateSplitters();
49206     },
49207
49208     generateRules : function(cm){
49209         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49210         Roo.util.CSS.removeStyleSheet(rulesId);
49211         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49212             var cid = cm.getColumnId(i);
49213             var align = '';
49214             if(cm.config[i].align){
49215                 align = 'text-align:'+cm.config[i].align+';';
49216             }
49217             var hidden = '';
49218             if(cm.isHidden(i)){
49219                 hidden = 'display:none;';
49220             }
49221             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49222             ruleBuf.push(
49223                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49224                     this.hdSelector, cid, " {\n", align, width, "}\n",
49225                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49226                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49227         }
49228         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49229     },
49230
49231     updateSplitters : function(){
49232         var cm = this.cm, s = this.getSplitters();
49233         if(s){ // splitters not created yet
49234             var pos = 0, locked = true;
49235             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49236                 if(cm.isHidden(i)) continue;
49237                 var w = cm.getColumnWidth(i); // make sure it's a number
49238                 if(!cm.isLocked(i) && locked){
49239                     pos = 0;
49240                     locked = false;
49241                 }
49242                 pos += w;
49243                 s[i].style.left = (pos-this.splitOffset) + "px";
49244             }
49245         }
49246     },
49247
49248     handleHiddenChange : function(colModel, colIndex, hidden){
49249         if(hidden){
49250             this.hideColumn(colIndex);
49251         }else{
49252             this.unhideColumn(colIndex);
49253         }
49254     },
49255
49256     hideColumn : function(colIndex){
49257         var cid = this.getColumnId(colIndex);
49258         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49259         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49260         if(Roo.isSafari){
49261             this.updateHeaders();
49262         }
49263         this.updateSplitters();
49264         this.layout();
49265     },
49266
49267     unhideColumn : function(colIndex){
49268         var cid = this.getColumnId(colIndex);
49269         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49270         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49271
49272         if(Roo.isSafari){
49273             this.updateHeaders();
49274         }
49275         this.updateSplitters();
49276         this.layout();
49277     },
49278
49279     insertRows : function(dm, firstRow, lastRow, isUpdate){
49280         if(firstRow == 0 && lastRow == dm.getCount()-1){
49281             this.refresh();
49282         }else{
49283             if(!isUpdate){
49284                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49285             }
49286             var s = this.getScrollState();
49287             var markup = this.renderRows(firstRow, lastRow);
49288             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49289             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49290             this.restoreScroll(s);
49291             if(!isUpdate){
49292                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49293                 this.syncRowHeights(firstRow, lastRow);
49294                 this.stripeRows(firstRow);
49295                 this.layout();
49296             }
49297         }
49298     },
49299
49300     bufferRows : function(markup, target, index){
49301         var before = null, trows = target.rows, tbody = target.tBodies[0];
49302         if(index < trows.length){
49303             before = trows[index];
49304         }
49305         var b = document.createElement("div");
49306         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49307         var rows = b.firstChild.rows;
49308         for(var i = 0, len = rows.length; i < len; i++){
49309             if(before){
49310                 tbody.insertBefore(rows[0], before);
49311             }else{
49312                 tbody.appendChild(rows[0]);
49313             }
49314         }
49315         b.innerHTML = "";
49316         b = null;
49317     },
49318
49319     deleteRows : function(dm, firstRow, lastRow){
49320         if(dm.getRowCount()<1){
49321             this.fireEvent("beforerefresh", this);
49322             this.mainBody.update("");
49323             this.lockedBody.update("");
49324             this.fireEvent("refresh", this);
49325         }else{
49326             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49327             var bt = this.getBodyTable();
49328             var tbody = bt.firstChild;
49329             var rows = bt.rows;
49330             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49331                 tbody.removeChild(rows[firstRow]);
49332             }
49333             this.stripeRows(firstRow);
49334             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49335         }
49336     },
49337
49338     updateRows : function(dataSource, firstRow, lastRow){
49339         var s = this.getScrollState();
49340         this.refresh();
49341         this.restoreScroll(s);
49342     },
49343
49344     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49345         if(!noRefresh){
49346            this.refresh();
49347         }
49348         this.updateHeaderSortState();
49349     },
49350
49351     getScrollState : function(){
49352         
49353         var sb = this.scroller.dom;
49354         return {left: sb.scrollLeft, top: sb.scrollTop};
49355     },
49356
49357     stripeRows : function(startRow){
49358         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49359             return;
49360         }
49361         startRow = startRow || 0;
49362         var rows = this.getBodyTable().rows;
49363         var lrows = this.getLockedTable().rows;
49364         var cls = ' x-grid-row-alt ';
49365         for(var i = startRow, len = rows.length; i < len; i++){
49366             var row = rows[i], lrow = lrows[i];
49367             var isAlt = ((i+1) % 2 == 0);
49368             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49369             if(isAlt == hasAlt){
49370                 continue;
49371             }
49372             if(isAlt){
49373                 row.className += " x-grid-row-alt";
49374             }else{
49375                 row.className = row.className.replace("x-grid-row-alt", "");
49376             }
49377             if(lrow){
49378                 lrow.className = row.className;
49379             }
49380         }
49381     },
49382
49383     restoreScroll : function(state){
49384         //Roo.log('GridView.restoreScroll');
49385         var sb = this.scroller.dom;
49386         sb.scrollLeft = state.left;
49387         sb.scrollTop = state.top;
49388         this.syncScroll();
49389     },
49390
49391     syncScroll : function(){
49392         //Roo.log('GridView.syncScroll');
49393         var sb = this.scroller.dom;
49394         var sh = this.mainHd.dom;
49395         var bs = this.mainBody.dom;
49396         var lv = this.lockedBody.dom;
49397         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49398         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49399     },
49400
49401     handleScroll : function(e){
49402         this.syncScroll();
49403         var sb = this.scroller.dom;
49404         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49405         e.stopEvent();
49406     },
49407
49408     handleWheel : function(e){
49409         var d = e.getWheelDelta();
49410         this.scroller.dom.scrollTop -= d*22;
49411         // set this here to prevent jumpy scrolling on large tables
49412         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49413         e.stopEvent();
49414     },
49415
49416     renderRows : function(startRow, endRow){
49417         // pull in all the crap needed to render rows
49418         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49419         var colCount = cm.getColumnCount();
49420
49421         if(ds.getCount() < 1){
49422             return ["", ""];
49423         }
49424
49425         // build a map for all the columns
49426         var cs = [];
49427         for(var i = 0; i < colCount; i++){
49428             var name = cm.getDataIndex(i);
49429             cs[i] = {
49430                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49431                 renderer : cm.getRenderer(i),
49432                 id : cm.getColumnId(i),
49433                 locked : cm.isLocked(i)
49434             };
49435         }
49436
49437         startRow = startRow || 0;
49438         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49439
49440         // records to render
49441         var rs = ds.getRange(startRow, endRow);
49442
49443         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49444     },
49445
49446     // As much as I hate to duplicate code, this was branched because FireFox really hates
49447     // [].join("") on strings. The performance difference was substantial enough to
49448     // branch this function
49449     doRender : Roo.isGecko ?
49450             function(cs, rs, ds, startRow, colCount, stripe){
49451                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49452                 // buffers
49453                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49454                 
49455                 var hasListener = this.grid.hasListener('rowclass');
49456                 var rowcfg = {};
49457                 for(var j = 0, len = rs.length; j < len; j++){
49458                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49459                     for(var i = 0; i < colCount; i++){
49460                         c = cs[i];
49461                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49462                         p.id = c.id;
49463                         p.css = p.attr = "";
49464                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49465                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49466                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49467                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49468                         }
49469                         var markup = ct.apply(p);
49470                         if(!c.locked){
49471                             cb+= markup;
49472                         }else{
49473                             lcb+= markup;
49474                         }
49475                     }
49476                     var alt = [];
49477                     if(stripe && ((rowIndex+1) % 2 == 0)){
49478                         alt.push("x-grid-row-alt")
49479                     }
49480                     if(r.dirty){
49481                         alt.push(  " x-grid-dirty-row");
49482                     }
49483                     rp.cells = lcb;
49484                     if(this.getRowClass){
49485                         alt.push(this.getRowClass(r, rowIndex));
49486                     }
49487                     if (hasListener) {
49488                         rowcfg = {
49489                              
49490                             record: r,
49491                             rowIndex : rowIndex,
49492                             rowClass : ''
49493                         }
49494                         this.grid.fireEvent('rowclass', this, rowcfg);
49495                         alt.push(rowcfg.rowClass);
49496                     }
49497                     rp.alt = alt.join(" ");
49498                     lbuf+= rt.apply(rp);
49499                     rp.cells = cb;
49500                     buf+=  rt.apply(rp);
49501                 }
49502                 return [lbuf, buf];
49503             } :
49504             function(cs, rs, ds, startRow, colCount, stripe){
49505                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49506                 // buffers
49507                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49508                 var hasListener = this.grid.hasListener('rowclass');
49509  
49510                 var rowcfg = {};
49511                 for(var j = 0, len = rs.length; j < len; j++){
49512                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49513                     for(var i = 0; i < colCount; i++){
49514                         c = cs[i];
49515                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49516                         p.id = c.id;
49517                         p.css = p.attr = "";
49518                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49519                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49520                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49521                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49522                         }
49523                         
49524                         var markup = ct.apply(p);
49525                         if(!c.locked){
49526                             cb[cb.length] = markup;
49527                         }else{
49528                             lcb[lcb.length] = markup;
49529                         }
49530                     }
49531                     var alt = [];
49532                     if(stripe && ((rowIndex+1) % 2 == 0)){
49533                         alt.push( "x-grid-row-alt");
49534                     }
49535                     if(r.dirty){
49536                         alt.push(" x-grid-dirty-row");
49537                     }
49538                     rp.cells = lcb;
49539                     if(this.getRowClass){
49540                         alt.push( this.getRowClass(r, rowIndex));
49541                     }
49542                     if (hasListener) {
49543                         rowcfg = {
49544                              
49545                             record: r,
49546                             rowIndex : rowIndex,
49547                             rowClass : ''
49548                         }
49549                         this.grid.fireEvent('rowclass', this, rowcfg);
49550                         alt.push(rowcfg.rowClass);
49551                     }
49552                     rp.alt = alt.join(" ");
49553                     rp.cells = lcb.join("");
49554                     lbuf[lbuf.length] = rt.apply(rp);
49555                     rp.cells = cb.join("");
49556                     buf[buf.length] =  rt.apply(rp);
49557                 }
49558                 return [lbuf.join(""), buf.join("")];
49559             },
49560
49561     renderBody : function(){
49562         var markup = this.renderRows();
49563         var bt = this.templates.body;
49564         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49565     },
49566
49567     /**
49568      * Refreshes the grid
49569      * @param {Boolean} headersToo
49570      */
49571     refresh : function(headersToo){
49572         this.fireEvent("beforerefresh", this);
49573         this.grid.stopEditing();
49574         var result = this.renderBody();
49575         this.lockedBody.update(result[0]);
49576         this.mainBody.update(result[1]);
49577         if(headersToo === true){
49578             this.updateHeaders();
49579             this.updateColumns();
49580             this.updateSplitters();
49581             this.updateHeaderSortState();
49582         }
49583         this.syncRowHeights();
49584         this.layout();
49585         this.fireEvent("refresh", this);
49586     },
49587
49588     handleColumnMove : function(cm, oldIndex, newIndex){
49589         this.indexMap = null;
49590         var s = this.getScrollState();
49591         this.refresh(true);
49592         this.restoreScroll(s);
49593         this.afterMove(newIndex);
49594     },
49595
49596     afterMove : function(colIndex){
49597         if(this.enableMoveAnim && Roo.enableFx){
49598             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49599         }
49600         // if multisort - fix sortOrder, and reload..
49601         if (this.grid.dataSource.multiSort) {
49602             // the we can call sort again..
49603             var dm = this.grid.dataSource;
49604             var cm = this.grid.colModel;
49605             var so = [];
49606             for(var i = 0; i < cm.config.length; i++ ) {
49607                 
49608                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49609                     continue; // dont' bother, it's not in sort list or being set.
49610                 }
49611                 
49612                 so.push(cm.config[i].dataIndex);
49613             };
49614             dm.sortOrder = so;
49615             dm.load(dm.lastOptions);
49616             
49617             
49618         }
49619         
49620     },
49621
49622     updateCell : function(dm, rowIndex, dataIndex){
49623         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49624         if(typeof colIndex == "undefined"){ // not present in grid
49625             return;
49626         }
49627         var cm = this.grid.colModel;
49628         var cell = this.getCell(rowIndex, colIndex);
49629         var cellText = this.getCellText(rowIndex, colIndex);
49630
49631         var p = {
49632             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49633             id : cm.getColumnId(colIndex),
49634             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49635         };
49636         var renderer = cm.getRenderer(colIndex);
49637         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49638         if(typeof val == "undefined" || val === "") val = "&#160;";
49639         cellText.innerHTML = val;
49640         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49641         this.syncRowHeights(rowIndex, rowIndex);
49642     },
49643
49644     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49645         var maxWidth = 0;
49646         if(this.grid.autoSizeHeaders){
49647             var h = this.getHeaderCellMeasure(colIndex);
49648             maxWidth = Math.max(maxWidth, h.scrollWidth);
49649         }
49650         var tb, index;
49651         if(this.cm.isLocked(colIndex)){
49652             tb = this.getLockedTable();
49653             index = colIndex;
49654         }else{
49655             tb = this.getBodyTable();
49656             index = colIndex - this.cm.getLockedCount();
49657         }
49658         if(tb && tb.rows){
49659             var rows = tb.rows;
49660             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49661             for(var i = 0; i < stopIndex; i++){
49662                 var cell = rows[i].childNodes[index].firstChild;
49663                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49664             }
49665         }
49666         return maxWidth + /*margin for error in IE*/ 5;
49667     },
49668     /**
49669      * Autofit a column to its content.
49670      * @param {Number} colIndex
49671      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49672      */
49673      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49674          if(this.cm.isHidden(colIndex)){
49675              return; // can't calc a hidden column
49676          }
49677         if(forceMinSize){
49678             var cid = this.cm.getColumnId(colIndex);
49679             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49680            if(this.grid.autoSizeHeaders){
49681                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49682            }
49683         }
49684         var newWidth = this.calcColumnWidth(colIndex);
49685         this.cm.setColumnWidth(colIndex,
49686             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49687         if(!suppressEvent){
49688             this.grid.fireEvent("columnresize", colIndex, newWidth);
49689         }
49690     },
49691
49692     /**
49693      * Autofits all columns to their content and then expands to fit any extra space in the grid
49694      */
49695      autoSizeColumns : function(){
49696         var cm = this.grid.colModel;
49697         var colCount = cm.getColumnCount();
49698         for(var i = 0; i < colCount; i++){
49699             this.autoSizeColumn(i, true, true);
49700         }
49701         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49702             this.fitColumns();
49703         }else{
49704             this.updateColumns();
49705             this.layout();
49706         }
49707     },
49708
49709     /**
49710      * Autofits all columns to the grid's width proportionate with their current size
49711      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49712      */
49713     fitColumns : function(reserveScrollSpace){
49714         var cm = this.grid.colModel;
49715         var colCount = cm.getColumnCount();
49716         var cols = [];
49717         var width = 0;
49718         var i, w;
49719         for (i = 0; i < colCount; i++){
49720             if(!cm.isHidden(i) && !cm.isFixed(i)){
49721                 w = cm.getColumnWidth(i);
49722                 cols.push(i);
49723                 cols.push(w);
49724                 width += w;
49725             }
49726         }
49727         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49728         if(reserveScrollSpace){
49729             avail -= 17;
49730         }
49731         var frac = (avail - cm.getTotalWidth())/width;
49732         while (cols.length){
49733             w = cols.pop();
49734             i = cols.pop();
49735             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49736         }
49737         this.updateColumns();
49738         this.layout();
49739     },
49740
49741     onRowSelect : function(rowIndex){
49742         var row = this.getRowComposite(rowIndex);
49743         row.addClass("x-grid-row-selected");
49744     },
49745
49746     onRowDeselect : function(rowIndex){
49747         var row = this.getRowComposite(rowIndex);
49748         row.removeClass("x-grid-row-selected");
49749     },
49750
49751     onCellSelect : function(row, col){
49752         var cell = this.getCell(row, col);
49753         if(cell){
49754             Roo.fly(cell).addClass("x-grid-cell-selected");
49755         }
49756     },
49757
49758     onCellDeselect : function(row, col){
49759         var cell = this.getCell(row, col);
49760         if(cell){
49761             Roo.fly(cell).removeClass("x-grid-cell-selected");
49762         }
49763     },
49764
49765     updateHeaderSortState : function(){
49766         
49767         // sort state can be single { field: xxx, direction : yyy}
49768         // or   { xxx=>ASC , yyy : DESC ..... }
49769         
49770         var mstate = {};
49771         if (!this.ds.multiSort) { 
49772             var state = this.ds.getSortState();
49773             if(!state){
49774                 return;
49775             }
49776             mstate[state.field] = state.direction;
49777             // FIXME... - this is not used here.. but might be elsewhere..
49778             this.sortState = state;
49779             
49780         } else {
49781             mstate = this.ds.sortToggle;
49782         }
49783         //remove existing sort classes..
49784         
49785         var sc = this.sortClasses;
49786         var hds = this.el.select(this.headerSelector).removeClass(sc);
49787         
49788         for(var f in mstate) {
49789         
49790             var sortColumn = this.cm.findColumnIndex(f);
49791             
49792             if(sortColumn != -1){
49793                 var sortDir = mstate[f];        
49794                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49795             }
49796         }
49797         
49798          
49799         
49800     },
49801
49802
49803     handleHeaderClick : function(g, index){
49804         if(this.headersDisabled){
49805             return;
49806         }
49807         var dm = g.dataSource, cm = g.colModel;
49808         if(!cm.isSortable(index)){
49809             return;
49810         }
49811         g.stopEditing();
49812         
49813         if (dm.multiSort) {
49814             // update the sortOrder
49815             var so = [];
49816             for(var i = 0; i < cm.config.length; i++ ) {
49817                 
49818                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49819                     continue; // dont' bother, it's not in sort list or being set.
49820                 }
49821                 
49822                 so.push(cm.config[i].dataIndex);
49823             };
49824             dm.sortOrder = so;
49825         }
49826         
49827         
49828         dm.sort(cm.getDataIndex(index));
49829     },
49830
49831
49832     destroy : function(){
49833         if(this.colMenu){
49834             this.colMenu.removeAll();
49835             Roo.menu.MenuMgr.unregister(this.colMenu);
49836             this.colMenu.getEl().remove();
49837             delete this.colMenu;
49838         }
49839         if(this.hmenu){
49840             this.hmenu.removeAll();
49841             Roo.menu.MenuMgr.unregister(this.hmenu);
49842             this.hmenu.getEl().remove();
49843             delete this.hmenu;
49844         }
49845         if(this.grid.enableColumnMove){
49846             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49847             if(dds){
49848                 for(var dd in dds){
49849                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49850                         var elid = dds[dd].dragElId;
49851                         dds[dd].unreg();
49852                         Roo.get(elid).remove();
49853                     } else if(dds[dd].config.isTarget){
49854                         dds[dd].proxyTop.remove();
49855                         dds[dd].proxyBottom.remove();
49856                         dds[dd].unreg();
49857                     }
49858                     if(Roo.dd.DDM.locationCache[dd]){
49859                         delete Roo.dd.DDM.locationCache[dd];
49860                     }
49861                 }
49862                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49863             }
49864         }
49865         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49866         this.bind(null, null);
49867         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49868     },
49869
49870     handleLockChange : function(){
49871         this.refresh(true);
49872     },
49873
49874     onDenyColumnLock : function(){
49875
49876     },
49877
49878     onDenyColumnHide : function(){
49879
49880     },
49881
49882     handleHdMenuClick : function(item){
49883         var index = this.hdCtxIndex;
49884         var cm = this.cm, ds = this.ds;
49885         switch(item.id){
49886             case "asc":
49887                 ds.sort(cm.getDataIndex(index), "ASC");
49888                 break;
49889             case "desc":
49890                 ds.sort(cm.getDataIndex(index), "DESC");
49891                 break;
49892             case "lock":
49893                 var lc = cm.getLockedCount();
49894                 if(cm.getColumnCount(true) <= lc+1){
49895                     this.onDenyColumnLock();
49896                     return;
49897                 }
49898                 if(lc != index){
49899                     cm.setLocked(index, true, true);
49900                     cm.moveColumn(index, lc);
49901                     this.grid.fireEvent("columnmove", index, lc);
49902                 }else{
49903                     cm.setLocked(index, true);
49904                 }
49905             break;
49906             case "unlock":
49907                 var lc = cm.getLockedCount();
49908                 if((lc-1) != index){
49909                     cm.setLocked(index, false, true);
49910                     cm.moveColumn(index, lc-1);
49911                     this.grid.fireEvent("columnmove", index, lc-1);
49912                 }else{
49913                     cm.setLocked(index, false);
49914                 }
49915             break;
49916             default:
49917                 index = cm.getIndexById(item.id.substr(4));
49918                 if(index != -1){
49919                     if(item.checked && cm.getColumnCount(true) <= 1){
49920                         this.onDenyColumnHide();
49921                         return false;
49922                     }
49923                     cm.setHidden(index, item.checked);
49924                 }
49925         }
49926         return true;
49927     },
49928
49929     beforeColMenuShow : function(){
49930         var cm = this.cm,  colCount = cm.getColumnCount();
49931         this.colMenu.removeAll();
49932         for(var i = 0; i < colCount; i++){
49933             this.colMenu.add(new Roo.menu.CheckItem({
49934                 id: "col-"+cm.getColumnId(i),
49935                 text: cm.getColumnHeader(i),
49936                 checked: !cm.isHidden(i),
49937                 hideOnClick:false
49938             }));
49939         }
49940     },
49941
49942     handleHdCtx : function(g, index, e){
49943         e.stopEvent();
49944         var hd = this.getHeaderCell(index);
49945         this.hdCtxIndex = index;
49946         var ms = this.hmenu.items, cm = this.cm;
49947         ms.get("asc").setDisabled(!cm.isSortable(index));
49948         ms.get("desc").setDisabled(!cm.isSortable(index));
49949         if(this.grid.enableColLock !== false){
49950             ms.get("lock").setDisabled(cm.isLocked(index));
49951             ms.get("unlock").setDisabled(!cm.isLocked(index));
49952         }
49953         this.hmenu.show(hd, "tl-bl");
49954     },
49955
49956     handleHdOver : function(e){
49957         var hd = this.findHeaderCell(e.getTarget());
49958         if(hd && !this.headersDisabled){
49959             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49960                this.fly(hd).addClass("x-grid-hd-over");
49961             }
49962         }
49963     },
49964
49965     handleHdOut : function(e){
49966         var hd = this.findHeaderCell(e.getTarget());
49967         if(hd){
49968             this.fly(hd).removeClass("x-grid-hd-over");
49969         }
49970     },
49971
49972     handleSplitDblClick : function(e, t){
49973         var i = this.getCellIndex(t);
49974         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49975             this.autoSizeColumn(i, true);
49976             this.layout();
49977         }
49978     },
49979
49980     render : function(){
49981
49982         var cm = this.cm;
49983         var colCount = cm.getColumnCount();
49984
49985         if(this.grid.monitorWindowResize === true){
49986             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49987         }
49988         var header = this.renderHeaders();
49989         var body = this.templates.body.apply({rows:""});
49990         var html = this.templates.master.apply({
49991             lockedBody: body,
49992             body: body,
49993             lockedHeader: header[0],
49994             header: header[1]
49995         });
49996
49997         //this.updateColumns();
49998
49999         this.grid.getGridEl().dom.innerHTML = html;
50000
50001         this.initElements();
50002         
50003         // a kludge to fix the random scolling effect in webkit
50004         this.el.on("scroll", function() {
50005             this.el.dom.scrollTop=0; // hopefully not recursive..
50006         },this);
50007
50008         this.scroller.on("scroll", this.handleScroll, this);
50009         this.lockedBody.on("mousewheel", this.handleWheel, this);
50010         this.mainBody.on("mousewheel", this.handleWheel, this);
50011
50012         this.mainHd.on("mouseover", this.handleHdOver, this);
50013         this.mainHd.on("mouseout", this.handleHdOut, this);
50014         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
50015                 {delegate: "."+this.splitClass});
50016
50017         this.lockedHd.on("mouseover", this.handleHdOver, this);
50018         this.lockedHd.on("mouseout", this.handleHdOut, this);
50019         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
50020                 {delegate: "."+this.splitClass});
50021
50022         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
50023             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50024         }
50025
50026         this.updateSplitters();
50027
50028         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
50029             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50030             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50031         }
50032
50033         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
50034             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
50035             this.hmenu.add(
50036                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
50037                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
50038             );
50039             if(this.grid.enableColLock !== false){
50040                 this.hmenu.add('-',
50041                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
50042                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
50043                 );
50044             }
50045             if(this.grid.enableColumnHide !== false){
50046
50047                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
50048                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
50049                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
50050
50051                 this.hmenu.add('-',
50052                     {id:"columns", text: this.columnsText, menu: this.colMenu}
50053                 );
50054             }
50055             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
50056
50057             this.grid.on("headercontextmenu", this.handleHdCtx, this);
50058         }
50059
50060         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
50061             this.dd = new Roo.grid.GridDragZone(this.grid, {
50062                 ddGroup : this.grid.ddGroup || 'GridDD'
50063             });
50064         }
50065
50066         /*
50067         for(var i = 0; i < colCount; i++){
50068             if(cm.isHidden(i)){
50069                 this.hideColumn(i);
50070             }
50071             if(cm.config[i].align){
50072                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
50073                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
50074             }
50075         }*/
50076         
50077         this.updateHeaderSortState();
50078
50079         this.beforeInitialResize();
50080         this.layout(true);
50081
50082         // two part rendering gives faster view to the user
50083         this.renderPhase2.defer(1, this);
50084     },
50085
50086     renderPhase2 : function(){
50087         // render the rows now
50088         this.refresh();
50089         if(this.grid.autoSizeColumns){
50090             this.autoSizeColumns();
50091         }
50092     },
50093
50094     beforeInitialResize : function(){
50095
50096     },
50097
50098     onColumnSplitterMoved : function(i, w){
50099         this.userResized = true;
50100         var cm = this.grid.colModel;
50101         cm.setColumnWidth(i, w, true);
50102         var cid = cm.getColumnId(i);
50103         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50104         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50105         this.updateSplitters();
50106         this.layout();
50107         this.grid.fireEvent("columnresize", i, w);
50108     },
50109
50110     syncRowHeights : function(startIndex, endIndex){
50111         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
50112             startIndex = startIndex || 0;
50113             var mrows = this.getBodyTable().rows;
50114             var lrows = this.getLockedTable().rows;
50115             var len = mrows.length-1;
50116             endIndex = Math.min(endIndex || len, len);
50117             for(var i = startIndex; i <= endIndex; i++){
50118                 var m = mrows[i], l = lrows[i];
50119                 var h = Math.max(m.offsetHeight, l.offsetHeight);
50120                 m.style.height = l.style.height = h + "px";
50121             }
50122         }
50123     },
50124
50125     layout : function(initialRender, is2ndPass){
50126         var g = this.grid;
50127         var auto = g.autoHeight;
50128         var scrollOffset = 16;
50129         var c = g.getGridEl(), cm = this.cm,
50130                 expandCol = g.autoExpandColumn,
50131                 gv = this;
50132         //c.beginMeasure();
50133
50134         if(!c.dom.offsetWidth){ // display:none?
50135             if(initialRender){
50136                 this.lockedWrap.show();
50137                 this.mainWrap.show();
50138             }
50139             return;
50140         }
50141
50142         var hasLock = this.cm.isLocked(0);
50143
50144         var tbh = this.headerPanel.getHeight();
50145         var bbh = this.footerPanel.getHeight();
50146
50147         if(auto){
50148             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
50149             var newHeight = ch + c.getBorderWidth("tb");
50150             if(g.maxHeight){
50151                 newHeight = Math.min(g.maxHeight, newHeight);
50152             }
50153             c.setHeight(newHeight);
50154         }
50155
50156         if(g.autoWidth){
50157             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
50158         }
50159
50160         var s = this.scroller;
50161
50162         var csize = c.getSize(true);
50163
50164         this.el.setSize(csize.width, csize.height);
50165
50166         this.headerPanel.setWidth(csize.width);
50167         this.footerPanel.setWidth(csize.width);
50168
50169         var hdHeight = this.mainHd.getHeight();
50170         var vw = csize.width;
50171         var vh = csize.height - (tbh + bbh);
50172
50173         s.setSize(vw, vh);
50174
50175         var bt = this.getBodyTable();
50176         var ltWidth = hasLock ?
50177                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50178
50179         var scrollHeight = bt.offsetHeight;
50180         var scrollWidth = ltWidth + bt.offsetWidth;
50181         var vscroll = false, hscroll = false;
50182
50183         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50184
50185         var lw = this.lockedWrap, mw = this.mainWrap;
50186         var lb = this.lockedBody, mb = this.mainBody;
50187
50188         setTimeout(function(){
50189             var t = s.dom.offsetTop;
50190             var w = s.dom.clientWidth,
50191                 h = s.dom.clientHeight;
50192
50193             lw.setTop(t);
50194             lw.setSize(ltWidth, h);
50195
50196             mw.setLeftTop(ltWidth, t);
50197             mw.setSize(w-ltWidth, h);
50198
50199             lb.setHeight(h-hdHeight);
50200             mb.setHeight(h-hdHeight);
50201
50202             if(is2ndPass !== true && !gv.userResized && expandCol){
50203                 // high speed resize without full column calculation
50204                 
50205                 var ci = cm.getIndexById(expandCol);
50206                 if (ci < 0) {
50207                     ci = cm.findColumnIndex(expandCol);
50208                 }
50209                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50210                 var expandId = cm.getColumnId(ci);
50211                 var  tw = cm.getTotalWidth(false);
50212                 var currentWidth = cm.getColumnWidth(ci);
50213                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50214                 if(currentWidth != cw){
50215                     cm.setColumnWidth(ci, cw, true);
50216                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50217                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50218                     gv.updateSplitters();
50219                     gv.layout(false, true);
50220                 }
50221             }
50222
50223             if(initialRender){
50224                 lw.show();
50225                 mw.show();
50226             }
50227             //c.endMeasure();
50228         }, 10);
50229     },
50230
50231     onWindowResize : function(){
50232         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50233             return;
50234         }
50235         this.layout();
50236     },
50237
50238     appendFooter : function(parentEl){
50239         return null;
50240     },
50241
50242     sortAscText : "Sort Ascending",
50243     sortDescText : "Sort Descending",
50244     lockText : "Lock Column",
50245     unlockText : "Unlock Column",
50246     columnsText : "Columns"
50247 });
50248
50249
50250 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50251     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50252     this.proxy.el.addClass('x-grid3-col-dd');
50253 };
50254
50255 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50256     handleMouseDown : function(e){
50257
50258     },
50259
50260     callHandleMouseDown : function(e){
50261         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50262     }
50263 });
50264 /*
50265  * Based on:
50266  * Ext JS Library 1.1.1
50267  * Copyright(c) 2006-2007, Ext JS, LLC.
50268  *
50269  * Originally Released Under LGPL - original licence link has changed is not relivant.
50270  *
50271  * Fork - LGPL
50272  * <script type="text/javascript">
50273  */
50274  
50275 // private
50276 // This is a support class used internally by the Grid components
50277 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50278     this.grid = grid;
50279     this.view = grid.getView();
50280     this.proxy = this.view.resizeProxy;
50281     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50282         "gridSplitters" + this.grid.getGridEl().id, {
50283         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50284     });
50285     this.setHandleElId(Roo.id(hd));
50286     this.setOuterHandleElId(Roo.id(hd2));
50287     this.scroll = false;
50288 };
50289 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50290     fly: Roo.Element.fly,
50291
50292     b4StartDrag : function(x, y){
50293         this.view.headersDisabled = true;
50294         this.proxy.setHeight(this.view.mainWrap.getHeight());
50295         var w = this.cm.getColumnWidth(this.cellIndex);
50296         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50297         this.resetConstraints();
50298         this.setXConstraint(minw, 1000);
50299         this.setYConstraint(0, 0);
50300         this.minX = x - minw;
50301         this.maxX = x + 1000;
50302         this.startPos = x;
50303         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50304     },
50305
50306
50307     handleMouseDown : function(e){
50308         ev = Roo.EventObject.setEvent(e);
50309         var t = this.fly(ev.getTarget());
50310         if(t.hasClass("x-grid-split")){
50311             this.cellIndex = this.view.getCellIndex(t.dom);
50312             this.split = t.dom;
50313             this.cm = this.grid.colModel;
50314             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50315                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50316             }
50317         }
50318     },
50319
50320     endDrag : function(e){
50321         this.view.headersDisabled = false;
50322         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50323         var diff = endX - this.startPos;
50324         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50325     },
50326
50327     autoOffset : function(){
50328         this.setDelta(0,0);
50329     }
50330 });/*
50331  * Based on:
50332  * Ext JS Library 1.1.1
50333  * Copyright(c) 2006-2007, Ext JS, LLC.
50334  *
50335  * Originally Released Under LGPL - original licence link has changed is not relivant.
50336  *
50337  * Fork - LGPL
50338  * <script type="text/javascript">
50339  */
50340  
50341 // private
50342 // This is a support class used internally by the Grid components
50343 Roo.grid.GridDragZone = function(grid, config){
50344     this.view = grid.getView();
50345     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50346     if(this.view.lockedBody){
50347         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50348         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50349     }
50350     this.scroll = false;
50351     this.grid = grid;
50352     this.ddel = document.createElement('div');
50353     this.ddel.className = 'x-grid-dd-wrap';
50354 };
50355
50356 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50357     ddGroup : "GridDD",
50358
50359     getDragData : function(e){
50360         var t = Roo.lib.Event.getTarget(e);
50361         var rowIndex = this.view.findRowIndex(t);
50362         if(rowIndex !== false){
50363             var sm = this.grid.selModel;
50364             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50365               //  sm.mouseDown(e, t);
50366             //}
50367             if (e.hasModifier()){
50368                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50369             }
50370             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50371         }
50372         return false;
50373     },
50374
50375     onInitDrag : function(e){
50376         var data = this.dragData;
50377         this.ddel.innerHTML = this.grid.getDragDropText();
50378         this.proxy.update(this.ddel);
50379         // fire start drag?
50380     },
50381
50382     afterRepair : function(){
50383         this.dragging = false;
50384     },
50385
50386     getRepairXY : function(e, data){
50387         return false;
50388     },
50389
50390     onEndDrag : function(data, e){
50391         // fire end drag?
50392     },
50393
50394     onValidDrop : function(dd, e, id){
50395         // fire drag drop?
50396         this.hideProxy();
50397     },
50398
50399     beforeInvalidDrop : function(e, id){
50400
50401     }
50402 });/*
50403  * Based on:
50404  * Ext JS Library 1.1.1
50405  * Copyright(c) 2006-2007, Ext JS, LLC.
50406  *
50407  * Originally Released Under LGPL - original licence link has changed is not relivant.
50408  *
50409  * Fork - LGPL
50410  * <script type="text/javascript">
50411  */
50412  
50413
50414 /**
50415  * @class Roo.grid.ColumnModel
50416  * @extends Roo.util.Observable
50417  * This is the default implementation of a ColumnModel used by the Grid. It defines
50418  * the columns in the grid.
50419  * <br>Usage:<br>
50420  <pre><code>
50421  var colModel = new Roo.grid.ColumnModel([
50422         {header: "Ticker", width: 60, sortable: true, locked: true},
50423         {header: "Company Name", width: 150, sortable: true},
50424         {header: "Market Cap.", width: 100, sortable: true},
50425         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50426         {header: "Employees", width: 100, sortable: true, resizable: false}
50427  ]);
50428  </code></pre>
50429  * <p>
50430  
50431  * The config options listed for this class are options which may appear in each
50432  * individual column definition.
50433  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50434  * @constructor
50435  * @param {Object} config An Array of column config objects. See this class's
50436  * config objects for details.
50437 */
50438 Roo.grid.ColumnModel = function(config){
50439         /**
50440      * The config passed into the constructor
50441      */
50442     this.config = config;
50443     this.lookup = {};
50444
50445     // if no id, create one
50446     // if the column does not have a dataIndex mapping,
50447     // map it to the order it is in the config
50448     for(var i = 0, len = config.length; i < len; i++){
50449         var c = config[i];
50450         if(typeof c.dataIndex == "undefined"){
50451             c.dataIndex = i;
50452         }
50453         if(typeof c.renderer == "string"){
50454             c.renderer = Roo.util.Format[c.renderer];
50455         }
50456         if(typeof c.id == "undefined"){
50457             c.id = Roo.id();
50458         }
50459         if(c.editor && c.editor.xtype){
50460             c.editor  = Roo.factory(c.editor, Roo.grid);
50461         }
50462         if(c.editor && c.editor.isFormField){
50463             c.editor = new Roo.grid.GridEditor(c.editor);
50464         }
50465         this.lookup[c.id] = c;
50466     }
50467
50468     /**
50469      * The width of columns which have no width specified (defaults to 100)
50470      * @type Number
50471      */
50472     this.defaultWidth = 100;
50473
50474     /**
50475      * Default sortable of columns which have no sortable specified (defaults to false)
50476      * @type Boolean
50477      */
50478     this.defaultSortable = false;
50479
50480     this.addEvents({
50481         /**
50482              * @event widthchange
50483              * Fires when the width of a column changes.
50484              * @param {ColumnModel} this
50485              * @param {Number} columnIndex The column index
50486              * @param {Number} newWidth The new width
50487              */
50488             "widthchange": true,
50489         /**
50490              * @event headerchange
50491              * Fires when the text of a header changes.
50492              * @param {ColumnModel} this
50493              * @param {Number} columnIndex The column index
50494              * @param {Number} newText The new header text
50495              */
50496             "headerchange": true,
50497         /**
50498              * @event hiddenchange
50499              * Fires when a column is hidden or "unhidden".
50500              * @param {ColumnModel} this
50501              * @param {Number} columnIndex The column index
50502              * @param {Boolean} hidden true if hidden, false otherwise
50503              */
50504             "hiddenchange": true,
50505             /**
50506          * @event columnmoved
50507          * Fires when a column is moved.
50508          * @param {ColumnModel} this
50509          * @param {Number} oldIndex
50510          * @param {Number} newIndex
50511          */
50512         "columnmoved" : true,
50513         /**
50514          * @event columlockchange
50515          * Fires when a column's locked state is changed
50516          * @param {ColumnModel} this
50517          * @param {Number} colIndex
50518          * @param {Boolean} locked true if locked
50519          */
50520         "columnlockchange" : true
50521     });
50522     Roo.grid.ColumnModel.superclass.constructor.call(this);
50523 };
50524 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50525     /**
50526      * @cfg {String} header The header text to display in the Grid view.
50527      */
50528     /**
50529      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50530      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50531      * specified, the column's index is used as an index into the Record's data Array.
50532      */
50533     /**
50534      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50535      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50536      */
50537     /**
50538      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50539      * Defaults to the value of the {@link #defaultSortable} property.
50540      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50541      */
50542     /**
50543      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50544      */
50545     /**
50546      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50547      */
50548     /**
50549      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50550      */
50551     /**
50552      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50553      */
50554     /**
50555      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50556      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50557      * default renderer uses the raw data value.
50558      */
50559        /**
50560      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50561      */
50562     /**
50563      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50564      */
50565
50566     /**
50567      * Returns the id of the column at the specified index.
50568      * @param {Number} index The column index
50569      * @return {String} the id
50570      */
50571     getColumnId : function(index){
50572         return this.config[index].id;
50573     },
50574
50575     /**
50576      * Returns the column for a specified id.
50577      * @param {String} id The column id
50578      * @return {Object} the column
50579      */
50580     getColumnById : function(id){
50581         return this.lookup[id];
50582     },
50583
50584     
50585     /**
50586      * Returns the column for a specified dataIndex.
50587      * @param {String} dataIndex The column dataIndex
50588      * @return {Object|Boolean} the column or false if not found
50589      */
50590     getColumnByDataIndex: function(dataIndex){
50591         var index = this.findColumnIndex(dataIndex);
50592         return index > -1 ? this.config[index] : false;
50593     },
50594     
50595     /**
50596      * Returns the index for a specified column id.
50597      * @param {String} id The column id
50598      * @return {Number} the index, or -1 if not found
50599      */
50600     getIndexById : function(id){
50601         for(var i = 0, len = this.config.length; i < len; i++){
50602             if(this.config[i].id == id){
50603                 return i;
50604             }
50605         }
50606         return -1;
50607     },
50608     
50609     /**
50610      * Returns the index for a specified column dataIndex.
50611      * @param {String} dataIndex The column dataIndex
50612      * @return {Number} the index, or -1 if not found
50613      */
50614     
50615     findColumnIndex : function(dataIndex){
50616         for(var i = 0, len = this.config.length; i < len; i++){
50617             if(this.config[i].dataIndex == dataIndex){
50618                 return i;
50619             }
50620         }
50621         return -1;
50622     },
50623     
50624     
50625     moveColumn : function(oldIndex, newIndex){
50626         var c = this.config[oldIndex];
50627         this.config.splice(oldIndex, 1);
50628         this.config.splice(newIndex, 0, c);
50629         this.dataMap = null;
50630         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50631     },
50632
50633     isLocked : function(colIndex){
50634         return this.config[colIndex].locked === true;
50635     },
50636
50637     setLocked : function(colIndex, value, suppressEvent){
50638         if(this.isLocked(colIndex) == value){
50639             return;
50640         }
50641         this.config[colIndex].locked = value;
50642         if(!suppressEvent){
50643             this.fireEvent("columnlockchange", this, colIndex, value);
50644         }
50645     },
50646
50647     getTotalLockedWidth : function(){
50648         var totalWidth = 0;
50649         for(var i = 0; i < this.config.length; i++){
50650             if(this.isLocked(i) && !this.isHidden(i)){
50651                 this.totalWidth += this.getColumnWidth(i);
50652             }
50653         }
50654         return totalWidth;
50655     },
50656
50657     getLockedCount : function(){
50658         for(var i = 0, len = this.config.length; i < len; i++){
50659             if(!this.isLocked(i)){
50660                 return i;
50661             }
50662         }
50663     },
50664
50665     /**
50666      * Returns the number of columns.
50667      * @return {Number}
50668      */
50669     getColumnCount : function(visibleOnly){
50670         if(visibleOnly === true){
50671             var c = 0;
50672             for(var i = 0, len = this.config.length; i < len; i++){
50673                 if(!this.isHidden(i)){
50674                     c++;
50675                 }
50676             }
50677             return c;
50678         }
50679         return this.config.length;
50680     },
50681
50682     /**
50683      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50684      * @param {Function} fn
50685      * @param {Object} scope (optional)
50686      * @return {Array} result
50687      */
50688     getColumnsBy : function(fn, scope){
50689         var r = [];
50690         for(var i = 0, len = this.config.length; i < len; i++){
50691             var c = this.config[i];
50692             if(fn.call(scope||this, c, i) === true){
50693                 r[r.length] = c;
50694             }
50695         }
50696         return r;
50697     },
50698
50699     /**
50700      * Returns true if the specified column is sortable.
50701      * @param {Number} col The column index
50702      * @return {Boolean}
50703      */
50704     isSortable : function(col){
50705         if(typeof this.config[col].sortable == "undefined"){
50706             return this.defaultSortable;
50707         }
50708         return this.config[col].sortable;
50709     },
50710
50711     /**
50712      * Returns the rendering (formatting) function defined for the column.
50713      * @param {Number} col The column index.
50714      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50715      */
50716     getRenderer : function(col){
50717         if(!this.config[col].renderer){
50718             return Roo.grid.ColumnModel.defaultRenderer;
50719         }
50720         return this.config[col].renderer;
50721     },
50722
50723     /**
50724      * Sets the rendering (formatting) function for a column.
50725      * @param {Number} col The column index
50726      * @param {Function} fn The function to use to process the cell's raw data
50727      * to return HTML markup for the grid view. The render function is called with
50728      * the following parameters:<ul>
50729      * <li>Data value.</li>
50730      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50731      * <li>css A CSS style string to apply to the table cell.</li>
50732      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50733      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50734      * <li>Row index</li>
50735      * <li>Column index</li>
50736      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50737      */
50738     setRenderer : function(col, fn){
50739         this.config[col].renderer = fn;
50740     },
50741
50742     /**
50743      * Returns the width for the specified column.
50744      * @param {Number} col The column index
50745      * @return {Number}
50746      */
50747     getColumnWidth : function(col){
50748         return this.config[col].width * 1 || this.defaultWidth;
50749     },
50750
50751     /**
50752      * Sets the width for a column.
50753      * @param {Number} col The column index
50754      * @param {Number} width The new width
50755      */
50756     setColumnWidth : function(col, width, suppressEvent){
50757         this.config[col].width = width;
50758         this.totalWidth = null;
50759         if(!suppressEvent){
50760              this.fireEvent("widthchange", this, col, width);
50761         }
50762     },
50763
50764     /**
50765      * Returns the total width of all columns.
50766      * @param {Boolean} includeHidden True to include hidden column widths
50767      * @return {Number}
50768      */
50769     getTotalWidth : function(includeHidden){
50770         if(!this.totalWidth){
50771             this.totalWidth = 0;
50772             for(var i = 0, len = this.config.length; i < len; i++){
50773                 if(includeHidden || !this.isHidden(i)){
50774                     this.totalWidth += this.getColumnWidth(i);
50775                 }
50776             }
50777         }
50778         return this.totalWidth;
50779     },
50780
50781     /**
50782      * Returns the header for the specified column.
50783      * @param {Number} col The column index
50784      * @return {String}
50785      */
50786     getColumnHeader : function(col){
50787         return this.config[col].header;
50788     },
50789
50790     /**
50791      * Sets the header for a column.
50792      * @param {Number} col The column index
50793      * @param {String} header The new header
50794      */
50795     setColumnHeader : function(col, header){
50796         this.config[col].header = header;
50797         this.fireEvent("headerchange", this, col, header);
50798     },
50799
50800     /**
50801      * Returns the tooltip for the specified column.
50802      * @param {Number} col The column index
50803      * @return {String}
50804      */
50805     getColumnTooltip : function(col){
50806             return this.config[col].tooltip;
50807     },
50808     /**
50809      * Sets the tooltip for a column.
50810      * @param {Number} col The column index
50811      * @param {String} tooltip The new tooltip
50812      */
50813     setColumnTooltip : function(col, tooltip){
50814             this.config[col].tooltip = tooltip;
50815     },
50816
50817     /**
50818      * Returns the dataIndex for the specified column.
50819      * @param {Number} col The column index
50820      * @return {Number}
50821      */
50822     getDataIndex : function(col){
50823         return this.config[col].dataIndex;
50824     },
50825
50826     /**
50827      * Sets the dataIndex for a column.
50828      * @param {Number} col The column index
50829      * @param {Number} dataIndex The new dataIndex
50830      */
50831     setDataIndex : function(col, dataIndex){
50832         this.config[col].dataIndex = dataIndex;
50833     },
50834
50835     
50836     
50837     /**
50838      * Returns true if the cell is editable.
50839      * @param {Number} colIndex The column index
50840      * @param {Number} rowIndex The row index
50841      * @return {Boolean}
50842      */
50843     isCellEditable : function(colIndex, rowIndex){
50844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50845     },
50846
50847     /**
50848      * Returns the editor defined for the cell/column.
50849      * return false or null to disable editing.
50850      * @param {Number} colIndex The column index
50851      * @param {Number} rowIndex The row index
50852      * @return {Object}
50853      */
50854     getCellEditor : function(colIndex, rowIndex){
50855         return this.config[colIndex].editor;
50856     },
50857
50858     /**
50859      * Sets if a column is editable.
50860      * @param {Number} col The column index
50861      * @param {Boolean} editable True if the column is editable
50862      */
50863     setEditable : function(col, editable){
50864         this.config[col].editable = editable;
50865     },
50866
50867
50868     /**
50869      * Returns true if the column is hidden.
50870      * @param {Number} colIndex The column index
50871      * @return {Boolean}
50872      */
50873     isHidden : function(colIndex){
50874         return this.config[colIndex].hidden;
50875     },
50876
50877
50878     /**
50879      * Returns true if the column width cannot be changed
50880      */
50881     isFixed : function(colIndex){
50882         return this.config[colIndex].fixed;
50883     },
50884
50885     /**
50886      * Returns true if the column can be resized
50887      * @return {Boolean}
50888      */
50889     isResizable : function(colIndex){
50890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50891     },
50892     /**
50893      * Sets if a column is hidden.
50894      * @param {Number} colIndex The column index
50895      * @param {Boolean} hidden True if the column is hidden
50896      */
50897     setHidden : function(colIndex, hidden){
50898         this.config[colIndex].hidden = hidden;
50899         this.totalWidth = null;
50900         this.fireEvent("hiddenchange", this, colIndex, hidden);
50901     },
50902
50903     /**
50904      * Sets the editor for a column.
50905      * @param {Number} col The column index
50906      * @param {Object} editor The editor object
50907      */
50908     setEditor : function(col, editor){
50909         this.config[col].editor = editor;
50910     }
50911 });
50912
50913 Roo.grid.ColumnModel.defaultRenderer = function(value){
50914         if(typeof value == "string" && value.length < 1){
50915             return "&#160;";
50916         }
50917         return value;
50918 };
50919
50920 // Alias for backwards compatibility
50921 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50922 /*
50923  * Based on:
50924  * Ext JS Library 1.1.1
50925  * Copyright(c) 2006-2007, Ext JS, LLC.
50926  *
50927  * Originally Released Under LGPL - original licence link has changed is not relivant.
50928  *
50929  * Fork - LGPL
50930  * <script type="text/javascript">
50931  */
50932
50933 /**
50934  * @class Roo.grid.AbstractSelectionModel
50935  * @extends Roo.util.Observable
50936  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50937  * implemented by descendant classes.  This class should not be directly instantiated.
50938  * @constructor
50939  */
50940 Roo.grid.AbstractSelectionModel = function(){
50941     this.locked = false;
50942     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50943 };
50944
50945 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50946     /** @ignore Called by the grid automatically. Do not call directly. */
50947     init : function(grid){
50948         this.grid = grid;
50949         this.initEvents();
50950     },
50951
50952     /**
50953      * Locks the selections.
50954      */
50955     lock : function(){
50956         this.locked = true;
50957     },
50958
50959     /**
50960      * Unlocks the selections.
50961      */
50962     unlock : function(){
50963         this.locked = false;
50964     },
50965
50966     /**
50967      * Returns true if the selections are locked.
50968      * @return {Boolean}
50969      */
50970     isLocked : function(){
50971         return this.locked;
50972     }
50973 });/*
50974  * Based on:
50975  * Ext JS Library 1.1.1
50976  * Copyright(c) 2006-2007, Ext JS, LLC.
50977  *
50978  * Originally Released Under LGPL - original licence link has changed is not relivant.
50979  *
50980  * Fork - LGPL
50981  * <script type="text/javascript">
50982  */
50983 /**
50984  * @extends Roo.grid.AbstractSelectionModel
50985  * @class Roo.grid.RowSelectionModel
50986  * The default SelectionModel used by {@link Roo.grid.Grid}.
50987  * It supports multiple selections and keyboard selection/navigation. 
50988  * @constructor
50989  * @param {Object} config
50990  */
50991 Roo.grid.RowSelectionModel = function(config){
50992     Roo.apply(this, config);
50993     this.selections = new Roo.util.MixedCollection(false, function(o){
50994         return o.id;
50995     });
50996
50997     this.last = false;
50998     this.lastActive = false;
50999
51000     this.addEvents({
51001         /**
51002              * @event selectionchange
51003              * Fires when the selection changes
51004              * @param {SelectionModel} this
51005              */
51006             "selectionchange" : true,
51007         /**
51008              * @event afterselectionchange
51009              * Fires after the selection changes (eg. by key press or clicking)
51010              * @param {SelectionModel} this
51011              */
51012             "afterselectionchange" : true,
51013         /**
51014              * @event beforerowselect
51015              * Fires when a row is selected being selected, return false to cancel.
51016              * @param {SelectionModel} this
51017              * @param {Number} rowIndex The selected index
51018              * @param {Boolean} keepExisting False if other selections will be cleared
51019              */
51020             "beforerowselect" : true,
51021         /**
51022              * @event rowselect
51023              * Fires when a row is selected.
51024              * @param {SelectionModel} this
51025              * @param {Number} rowIndex The selected index
51026              * @param {Roo.data.Record} r The record
51027              */
51028             "rowselect" : true,
51029         /**
51030              * @event rowdeselect
51031              * Fires when a row is deselected.
51032              * @param {SelectionModel} this
51033              * @param {Number} rowIndex The selected index
51034              */
51035         "rowdeselect" : true
51036     });
51037     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
51038     this.locked = false;
51039 };
51040
51041 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
51042     /**
51043      * @cfg {Boolean} singleSelect
51044      * True to allow selection of only one row at a time (defaults to false)
51045      */
51046     singleSelect : false,
51047
51048     // private
51049     initEvents : function(){
51050
51051         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
51052             this.grid.on("mousedown", this.handleMouseDown, this);
51053         }else{ // allow click to work like normal
51054             this.grid.on("rowclick", this.handleDragableRowClick, this);
51055         }
51056
51057         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
51058             "up" : function(e){
51059                 if(!e.shiftKey){
51060                     this.selectPrevious(e.shiftKey);
51061                 }else if(this.last !== false && this.lastActive !== false){
51062                     var last = this.last;
51063                     this.selectRange(this.last,  this.lastActive-1);
51064                     this.grid.getView().focusRow(this.lastActive);
51065                     if(last !== false){
51066                         this.last = last;
51067                     }
51068                 }else{
51069                     this.selectFirstRow();
51070                 }
51071                 this.fireEvent("afterselectionchange", this);
51072             },
51073             "down" : function(e){
51074                 if(!e.shiftKey){
51075                     this.selectNext(e.shiftKey);
51076                 }else if(this.last !== false && this.lastActive !== false){
51077                     var last = this.last;
51078                     this.selectRange(this.last,  this.lastActive+1);
51079                     this.grid.getView().focusRow(this.lastActive);
51080                     if(last !== false){
51081                         this.last = last;
51082                     }
51083                 }else{
51084                     this.selectFirstRow();
51085                 }
51086                 this.fireEvent("afterselectionchange", this);
51087             },
51088             scope: this
51089         });
51090
51091         var view = this.grid.view;
51092         view.on("refresh", this.onRefresh, this);
51093         view.on("rowupdated", this.onRowUpdated, this);
51094         view.on("rowremoved", this.onRemove, this);
51095     },
51096
51097     // private
51098     onRefresh : function(){
51099         var ds = this.grid.dataSource, i, v = this.grid.view;
51100         var s = this.selections;
51101         s.each(function(r){
51102             if((i = ds.indexOfId(r.id)) != -1){
51103                 v.onRowSelect(i);
51104             }else{
51105                 s.remove(r);
51106             }
51107         });
51108     },
51109
51110     // private
51111     onRemove : function(v, index, r){
51112         this.selections.remove(r);
51113     },
51114
51115     // private
51116     onRowUpdated : function(v, index, r){
51117         if(this.isSelected(r)){
51118             v.onRowSelect(index);
51119         }
51120     },
51121
51122     /**
51123      * Select records.
51124      * @param {Array} records The records to select
51125      * @param {Boolean} keepExisting (optional) True to keep existing selections
51126      */
51127     selectRecords : function(records, keepExisting){
51128         if(!keepExisting){
51129             this.clearSelections();
51130         }
51131         var ds = this.grid.dataSource;
51132         for(var i = 0, len = records.length; i < len; i++){
51133             this.selectRow(ds.indexOf(records[i]), true);
51134         }
51135     },
51136
51137     /**
51138      * Gets the number of selected rows.
51139      * @return {Number}
51140      */
51141     getCount : function(){
51142         return this.selections.length;
51143     },
51144
51145     /**
51146      * Selects the first row in the grid.
51147      */
51148     selectFirstRow : function(){
51149         this.selectRow(0);
51150     },
51151
51152     /**
51153      * Select the last row.
51154      * @param {Boolean} keepExisting (optional) True to keep existing selections
51155      */
51156     selectLastRow : function(keepExisting){
51157         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
51158     },
51159
51160     /**
51161      * Selects the row immediately following the last selected row.
51162      * @param {Boolean} keepExisting (optional) True to keep existing selections
51163      */
51164     selectNext : function(keepExisting){
51165         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
51166             this.selectRow(this.last+1, keepExisting);
51167             this.grid.getView().focusRow(this.last);
51168         }
51169     },
51170
51171     /**
51172      * Selects the row that precedes the last selected row.
51173      * @param {Boolean} keepExisting (optional) True to keep existing selections
51174      */
51175     selectPrevious : function(keepExisting){
51176         if(this.last){
51177             this.selectRow(this.last-1, keepExisting);
51178             this.grid.getView().focusRow(this.last);
51179         }
51180     },
51181
51182     /**
51183      * Returns the selected records
51184      * @return {Array} Array of selected records
51185      */
51186     getSelections : function(){
51187         return [].concat(this.selections.items);
51188     },
51189
51190     /**
51191      * Returns the first selected record.
51192      * @return {Record}
51193      */
51194     getSelected : function(){
51195         return this.selections.itemAt(0);
51196     },
51197
51198
51199     /**
51200      * Clears all selections.
51201      */
51202     clearSelections : function(fast){
51203         if(this.locked) return;
51204         if(fast !== true){
51205             var ds = this.grid.dataSource;
51206             var s = this.selections;
51207             s.each(function(r){
51208                 this.deselectRow(ds.indexOfId(r.id));
51209             }, this);
51210             s.clear();
51211         }else{
51212             this.selections.clear();
51213         }
51214         this.last = false;
51215     },
51216
51217
51218     /**
51219      * Selects all rows.
51220      */
51221     selectAll : function(){
51222         if(this.locked) return;
51223         this.selections.clear();
51224         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51225             this.selectRow(i, true);
51226         }
51227     },
51228
51229     /**
51230      * Returns True if there is a selection.
51231      * @return {Boolean}
51232      */
51233     hasSelection : function(){
51234         return this.selections.length > 0;
51235     },
51236
51237     /**
51238      * Returns True if the specified row is selected.
51239      * @param {Number/Record} record The record or index of the record to check
51240      * @return {Boolean}
51241      */
51242     isSelected : function(index){
51243         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51244         return (r && this.selections.key(r.id) ? true : false);
51245     },
51246
51247     /**
51248      * Returns True if the specified record id is selected.
51249      * @param {String} id The id of record to check
51250      * @return {Boolean}
51251      */
51252     isIdSelected : function(id){
51253         return (this.selections.key(id) ? true : false);
51254     },
51255
51256     // private
51257     handleMouseDown : function(e, t){
51258         var view = this.grid.getView(), rowIndex;
51259         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51260             return;
51261         };
51262         if(e.shiftKey && this.last !== false){
51263             var last = this.last;
51264             this.selectRange(last, rowIndex, e.ctrlKey);
51265             this.last = last; // reset the last
51266             view.focusRow(rowIndex);
51267         }else{
51268             var isSelected = this.isSelected(rowIndex);
51269             if(e.button !== 0 && isSelected){
51270                 view.focusRow(rowIndex);
51271             }else if(e.ctrlKey && isSelected){
51272                 this.deselectRow(rowIndex);
51273             }else if(!isSelected){
51274                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51275                 view.focusRow(rowIndex);
51276             }
51277         }
51278         this.fireEvent("afterselectionchange", this);
51279     },
51280     // private
51281     handleDragableRowClick :  function(grid, rowIndex, e) 
51282     {
51283         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51284             this.selectRow(rowIndex, false);
51285             grid.view.focusRow(rowIndex);
51286              this.fireEvent("afterselectionchange", this);
51287         }
51288     },
51289     
51290     /**
51291      * Selects multiple rows.
51292      * @param {Array} rows Array of the indexes of the row to select
51293      * @param {Boolean} keepExisting (optional) True to keep existing selections
51294      */
51295     selectRows : function(rows, keepExisting){
51296         if(!keepExisting){
51297             this.clearSelections();
51298         }
51299         for(var i = 0, len = rows.length; i < len; i++){
51300             this.selectRow(rows[i], true);
51301         }
51302     },
51303
51304     /**
51305      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51306      * @param {Number} startRow The index of the first row in the range
51307      * @param {Number} endRow The index of the last row in the range
51308      * @param {Boolean} keepExisting (optional) True to retain existing selections
51309      */
51310     selectRange : function(startRow, endRow, keepExisting){
51311         if(this.locked) return;
51312         if(!keepExisting){
51313             this.clearSelections();
51314         }
51315         if(startRow <= endRow){
51316             for(var i = startRow; i <= endRow; i++){
51317                 this.selectRow(i, true);
51318             }
51319         }else{
51320             for(var i = startRow; i >= endRow; i--){
51321                 this.selectRow(i, true);
51322             }
51323         }
51324     },
51325
51326     /**
51327      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51328      * @param {Number} startRow The index of the first row in the range
51329      * @param {Number} endRow The index of the last row in the range
51330      */
51331     deselectRange : function(startRow, endRow, preventViewNotify){
51332         if(this.locked) return;
51333         for(var i = startRow; i <= endRow; i++){
51334             this.deselectRow(i, preventViewNotify);
51335         }
51336     },
51337
51338     /**
51339      * Selects a row.
51340      * @param {Number} row The index of the row to select
51341      * @param {Boolean} keepExisting (optional) True to keep existing selections
51342      */
51343     selectRow : function(index, keepExisting, preventViewNotify){
51344         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51345         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51346             if(!keepExisting || this.singleSelect){
51347                 this.clearSelections();
51348             }
51349             var r = this.grid.dataSource.getAt(index);
51350             this.selections.add(r);
51351             this.last = this.lastActive = index;
51352             if(!preventViewNotify){
51353                 this.grid.getView().onRowSelect(index);
51354             }
51355             this.fireEvent("rowselect", this, index, r);
51356             this.fireEvent("selectionchange", this);
51357         }
51358     },
51359
51360     /**
51361      * Deselects a row.
51362      * @param {Number} row The index of the row to deselect
51363      */
51364     deselectRow : function(index, preventViewNotify){
51365         if(this.locked) return;
51366         if(this.last == index){
51367             this.last = false;
51368         }
51369         if(this.lastActive == index){
51370             this.lastActive = false;
51371         }
51372         var r = this.grid.dataSource.getAt(index);
51373         this.selections.remove(r);
51374         if(!preventViewNotify){
51375             this.grid.getView().onRowDeselect(index);
51376         }
51377         this.fireEvent("rowdeselect", this, index);
51378         this.fireEvent("selectionchange", this);
51379     },
51380
51381     // private
51382     restoreLast : function(){
51383         if(this._last){
51384             this.last = this._last;
51385         }
51386     },
51387
51388     // private
51389     acceptsNav : function(row, col, cm){
51390         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51391     },
51392
51393     // private
51394     onEditorKey : function(field, e){
51395         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51396         if(k == e.TAB){
51397             e.stopEvent();
51398             ed.completeEdit();
51399             if(e.shiftKey){
51400                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51401             }else{
51402                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51403             }
51404         }else if(k == e.ENTER && !e.ctrlKey){
51405             e.stopEvent();
51406             ed.completeEdit();
51407             if(e.shiftKey){
51408                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51409             }else{
51410                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51411             }
51412         }else if(k == e.ESC){
51413             ed.cancelEdit();
51414         }
51415         if(newCell){
51416             g.startEditing(newCell[0], newCell[1]);
51417         }
51418     }
51419 });/*
51420  * Based on:
51421  * Ext JS Library 1.1.1
51422  * Copyright(c) 2006-2007, Ext JS, LLC.
51423  *
51424  * Originally Released Under LGPL - original licence link has changed is not relivant.
51425  *
51426  * Fork - LGPL
51427  * <script type="text/javascript">
51428  */
51429 /**
51430  * @class Roo.grid.CellSelectionModel
51431  * @extends Roo.grid.AbstractSelectionModel
51432  * This class provides the basic implementation for cell selection in a grid.
51433  * @constructor
51434  * @param {Object} config The object containing the configuration of this model.
51435  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51436  */
51437 Roo.grid.CellSelectionModel = function(config){
51438     Roo.apply(this, config);
51439
51440     this.selection = null;
51441
51442     this.addEvents({
51443         /**
51444              * @event beforerowselect
51445              * Fires before a cell is selected.
51446              * @param {SelectionModel} this
51447              * @param {Number} rowIndex The selected row index
51448              * @param {Number} colIndex The selected cell index
51449              */
51450             "beforecellselect" : true,
51451         /**
51452              * @event cellselect
51453              * Fires when a cell is selected.
51454              * @param {SelectionModel} this
51455              * @param {Number} rowIndex The selected row index
51456              * @param {Number} colIndex The selected cell index
51457              */
51458             "cellselect" : true,
51459         /**
51460              * @event selectionchange
51461              * Fires when the active selection changes.
51462              * @param {SelectionModel} this
51463              * @param {Object} selection null for no selection or an object (o) with two properties
51464                 <ul>
51465                 <li>o.record: the record object for the row the selection is in</li>
51466                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51467                 </ul>
51468              */
51469             "selectionchange" : true,
51470         /**
51471              * @event tabend
51472              * Fires when the tab (or enter) was pressed on the last editable cell
51473              * You can use this to trigger add new row.
51474              * @param {SelectionModel} this
51475              */
51476             "tabend" : true,
51477          /**
51478              * @event beforeeditnext
51479              * Fires before the next editable sell is made active
51480              * You can use this to skip to another cell or fire the tabend
51481              *    if you set cell to false
51482              * @param {Object} eventdata object : { cell : [ row, col ] } 
51483              */
51484             "beforeeditnext" : true
51485     });
51486     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51487 };
51488
51489 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51490     
51491     enter_is_tab: false,
51492
51493     /** @ignore */
51494     initEvents : function(){
51495         this.grid.on("mousedown", this.handleMouseDown, this);
51496         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51497         var view = this.grid.view;
51498         view.on("refresh", this.onViewChange, this);
51499         view.on("rowupdated", this.onRowUpdated, this);
51500         view.on("beforerowremoved", this.clearSelections, this);
51501         view.on("beforerowsinserted", this.clearSelections, this);
51502         if(this.grid.isEditor){
51503             this.grid.on("beforeedit", this.beforeEdit,  this);
51504         }
51505     },
51506
51507         //private
51508     beforeEdit : function(e){
51509         this.select(e.row, e.column, false, true, e.record);
51510     },
51511
51512         //private
51513     onRowUpdated : function(v, index, r){
51514         if(this.selection && this.selection.record == r){
51515             v.onCellSelect(index, this.selection.cell[1]);
51516         }
51517     },
51518
51519         //private
51520     onViewChange : function(){
51521         this.clearSelections(true);
51522     },
51523
51524         /**
51525          * Returns the currently selected cell,.
51526          * @return {Array} The selected cell (row, column) or null if none selected.
51527          */
51528     getSelectedCell : function(){
51529         return this.selection ? this.selection.cell : null;
51530     },
51531
51532     /**
51533      * Clears all selections.
51534      * @param {Boolean} true to prevent the gridview from being notified about the change.
51535      */
51536     clearSelections : function(preventNotify){
51537         var s = this.selection;
51538         if(s){
51539             if(preventNotify !== true){
51540                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51541             }
51542             this.selection = null;
51543             this.fireEvent("selectionchange", this, null);
51544         }
51545     },
51546
51547     /**
51548      * Returns true if there is a selection.
51549      * @return {Boolean}
51550      */
51551     hasSelection : function(){
51552         return this.selection ? true : false;
51553     },
51554
51555     /** @ignore */
51556     handleMouseDown : function(e, t){
51557         var v = this.grid.getView();
51558         if(this.isLocked()){
51559             return;
51560         };
51561         var row = v.findRowIndex(t);
51562         var cell = v.findCellIndex(t);
51563         if(row !== false && cell !== false){
51564             this.select(row, cell);
51565         }
51566     },
51567
51568     /**
51569      * Selects a cell.
51570      * @param {Number} rowIndex
51571      * @param {Number} collIndex
51572      */
51573     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51574         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51575             this.clearSelections();
51576             r = r || this.grid.dataSource.getAt(rowIndex);
51577             this.selection = {
51578                 record : r,
51579                 cell : [rowIndex, colIndex]
51580             };
51581             if(!preventViewNotify){
51582                 var v = this.grid.getView();
51583                 v.onCellSelect(rowIndex, colIndex);
51584                 if(preventFocus !== true){
51585                     v.focusCell(rowIndex, colIndex);
51586                 }
51587             }
51588             this.fireEvent("cellselect", this, rowIndex, colIndex);
51589             this.fireEvent("selectionchange", this, this.selection);
51590         }
51591     },
51592
51593         //private
51594     isSelectable : function(rowIndex, colIndex, cm){
51595         return !cm.isHidden(colIndex);
51596     },
51597
51598     /** @ignore */
51599     handleKeyDown : function(e){
51600         //Roo.log('Cell Sel Model handleKeyDown');
51601         if(!e.isNavKeyPress()){
51602             return;
51603         }
51604         var g = this.grid, s = this.selection;
51605         if(!s){
51606             e.stopEvent();
51607             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51608             if(cell){
51609                 this.select(cell[0], cell[1]);
51610             }
51611             return;
51612         }
51613         var sm = this;
51614         var walk = function(row, col, step){
51615             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51616         };
51617         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51618         var newCell;
51619
51620       
51621
51622         switch(k){
51623             case e.TAB:
51624                 // handled by onEditorKey
51625                 if (g.isEditor && g.editing) {
51626                     return;
51627                 }
51628                 if(e.shiftKey) {
51629                     newCell = walk(r, c-1, -1);
51630                 } else {
51631                     newCell = walk(r, c+1, 1);
51632                 }
51633                 break;
51634             
51635             case e.DOWN:
51636                newCell = walk(r+1, c, 1);
51637                 break;
51638             
51639             case e.UP:
51640                 newCell = walk(r-1, c, -1);
51641                 break;
51642             
51643             case e.RIGHT:
51644                 newCell = walk(r, c+1, 1);
51645                 break;
51646             
51647             case e.LEFT:
51648                 newCell = walk(r, c-1, -1);
51649                 break;
51650             
51651             case e.ENTER:
51652                 
51653                 if(g.isEditor && !g.editing){
51654                    g.startEditing(r, c);
51655                    e.stopEvent();
51656                    return;
51657                 }
51658                 
51659                 
51660              break;
51661         };
51662         if(newCell){
51663             this.select(newCell[0], newCell[1]);
51664             e.stopEvent();
51665             
51666         }
51667     },
51668
51669     acceptsNav : function(row, col, cm){
51670         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51671     },
51672     /**
51673      * Selects a cell.
51674      * @param {Number} field (not used) - as it's normally used as a listener
51675      * @param {Number} e - event - fake it by using
51676      *
51677      * var e = Roo.EventObjectImpl.prototype;
51678      * e.keyCode = e.TAB
51679      *
51680      * 
51681      */
51682     onEditorKey : function(field, e){
51683         
51684         var k = e.getKey(),
51685             newCell,
51686             g = this.grid,
51687             ed = g.activeEditor,
51688             forward = false;
51689         ///Roo.log('onEditorKey' + k);
51690         
51691         
51692         if (this.enter_is_tab && k == e.ENTER) {
51693             k = e.TAB;
51694         }
51695         
51696         if(k == e.TAB){
51697             if(e.shiftKey){
51698                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51699             }else{
51700                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51701                 forward = true;
51702             }
51703             
51704             e.stopEvent();
51705             
51706         } else if(k == e.ENTER &&  !e.ctrlKey){
51707             ed.completeEdit();
51708             e.stopEvent();
51709             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51710         
51711                 } else if(k == e.ESC){
51712             ed.cancelEdit();
51713         }
51714                 
51715         if (newCell) {
51716             var ecall = { cell : newCell, forward : forward };
51717             this.fireEvent('beforeeditnext', ecall );
51718             newCell = ecall.cell;
51719                         forward = ecall.forward;
51720         }
51721                 
51722         if(newCell){
51723             //Roo.log('next cell after edit');
51724             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51725         } else if (forward) {
51726             // tabbed past last
51727             this.fireEvent.defer(100, this, ['tabend',this]);
51728         }
51729     }
51730 });/*
51731  * Based on:
51732  * Ext JS Library 1.1.1
51733  * Copyright(c) 2006-2007, Ext JS, LLC.
51734  *
51735  * Originally Released Under LGPL - original licence link has changed is not relivant.
51736  *
51737  * Fork - LGPL
51738  * <script type="text/javascript">
51739  */
51740  
51741 /**
51742  * @class Roo.grid.EditorGrid
51743  * @extends Roo.grid.Grid
51744  * Class for creating and editable grid.
51745  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51746  * The container MUST have some type of size defined for the grid to fill. The container will be 
51747  * automatically set to position relative if it isn't already.
51748  * @param {Object} dataSource The data model to bind to
51749  * @param {Object} colModel The column model with info about this grid's columns
51750  */
51751 Roo.grid.EditorGrid = function(container, config){
51752     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51753     this.getGridEl().addClass("xedit-grid");
51754
51755     if(!this.selModel){
51756         this.selModel = new Roo.grid.CellSelectionModel();
51757     }
51758
51759     this.activeEditor = null;
51760
51761         this.addEvents({
51762             /**
51763              * @event beforeedit
51764              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51765              * <ul style="padding:5px;padding-left:16px;">
51766              * <li>grid - This grid</li>
51767              * <li>record - The record being edited</li>
51768              * <li>field - The field name being edited</li>
51769              * <li>value - The value for the field being edited.</li>
51770              * <li>row - The grid row index</li>
51771              * <li>column - The grid column index</li>
51772              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51773              * </ul>
51774              * @param {Object} e An edit event (see above for description)
51775              */
51776             "beforeedit" : true,
51777             /**
51778              * @event afteredit
51779              * Fires after a cell is edited. <br />
51780              * <ul style="padding:5px;padding-left:16px;">
51781              * <li>grid - This grid</li>
51782              * <li>record - The record being edited</li>
51783              * <li>field - The field name being edited</li>
51784              * <li>value - The value being set</li>
51785              * <li>originalValue - The original value for the field, before the edit.</li>
51786              * <li>row - The grid row index</li>
51787              * <li>column - The grid column index</li>
51788              * </ul>
51789              * @param {Object} e An edit event (see above for description)
51790              */
51791             "afteredit" : true,
51792             /**
51793              * @event validateedit
51794              * Fires after a cell is edited, but before the value is set in the record. 
51795          * You can use this to modify the value being set in the field, Return false
51796              * to cancel the change. The edit event object has the following properties <br />
51797              * <ul style="padding:5px;padding-left:16px;">
51798          * <li>editor - This editor</li>
51799              * <li>grid - This grid</li>
51800              * <li>record - The record being edited</li>
51801              * <li>field - The field name being edited</li>
51802              * <li>value - The value being set</li>
51803              * <li>originalValue - The original value for the field, before the edit.</li>
51804              * <li>row - The grid row index</li>
51805              * <li>column - The grid column index</li>
51806              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51807              * </ul>
51808              * @param {Object} e An edit event (see above for description)
51809              */
51810             "validateedit" : true
51811         });
51812     this.on("bodyscroll", this.stopEditing,  this);
51813     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51814 };
51815
51816 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51817     /**
51818      * @cfg {Number} clicksToEdit
51819      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51820      */
51821     clicksToEdit: 2,
51822
51823     // private
51824     isEditor : true,
51825     // private
51826     trackMouseOver: false, // causes very odd FF errors
51827
51828     onCellDblClick : function(g, row, col){
51829         this.startEditing(row, col);
51830     },
51831
51832     onEditComplete : function(ed, value, startValue){
51833         this.editing = false;
51834         this.activeEditor = null;
51835         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51836         var r = ed.record;
51837         var field = this.colModel.getDataIndex(ed.col);
51838         var e = {
51839             grid: this,
51840             record: r,
51841             field: field,
51842             originalValue: startValue,
51843             value: value,
51844             row: ed.row,
51845             column: ed.col,
51846             cancel:false,
51847             editor: ed
51848         };
51849         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51850         cell.show();
51851           
51852         if(String(value) !== String(startValue)){
51853             
51854             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51855                 r.set(field, e.value);
51856                 // if we are dealing with a combo box..
51857                 // then we also set the 'name' colum to be the displayField
51858                 if (ed.field.displayField && ed.field.name) {
51859                     r.set(ed.field.name, ed.field.el.dom.value);
51860                 }
51861                 
51862                 delete e.cancel; //?? why!!!
51863                 this.fireEvent("afteredit", e);
51864             }
51865         } else {
51866             this.fireEvent("afteredit", e); // always fire it!
51867         }
51868         this.view.focusCell(ed.row, ed.col);
51869     },
51870
51871     /**
51872      * Starts editing the specified for the specified row/column
51873      * @param {Number} rowIndex
51874      * @param {Number} colIndex
51875      */
51876     startEditing : function(row, col){
51877         this.stopEditing();
51878         if(this.colModel.isCellEditable(col, row)){
51879             this.view.ensureVisible(row, col, true);
51880           
51881             var r = this.dataSource.getAt(row);
51882             var field = this.colModel.getDataIndex(col);
51883             var cell = Roo.get(this.view.getCell(row,col));
51884             var e = {
51885                 grid: this,
51886                 record: r,
51887                 field: field,
51888                 value: r.data[field],
51889                 row: row,
51890                 column: col,
51891                 cancel:false 
51892             };
51893             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51894                 this.editing = true;
51895                 var ed = this.colModel.getCellEditor(col, row);
51896                 
51897                 if (!ed) {
51898                     return;
51899                 }
51900                 if(!ed.rendered){
51901                     ed.render(ed.parentEl || document.body);
51902                 }
51903                 ed.field.reset();
51904                
51905                 cell.hide();
51906                 
51907                 (function(){ // complex but required for focus issues in safari, ie and opera
51908                     ed.row = row;
51909                     ed.col = col;
51910                     ed.record = r;
51911                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51912                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51913                     this.activeEditor = ed;
51914                     var v = r.data[field];
51915                     ed.startEdit(this.view.getCell(row, col), v);
51916                     // combo's with 'displayField and name set
51917                     if (ed.field.displayField && ed.field.name) {
51918                         ed.field.el.dom.value = r.data[ed.field.name];
51919                     }
51920                     
51921                     
51922                 }).defer(50, this);
51923             }
51924         }
51925     },
51926         
51927     /**
51928      * Stops any active editing
51929      */
51930     stopEditing : function(){
51931         if(this.activeEditor){
51932             this.activeEditor.completeEdit();
51933         }
51934         this.activeEditor = null;
51935     }
51936 });/*
51937  * Based on:
51938  * Ext JS Library 1.1.1
51939  * Copyright(c) 2006-2007, Ext JS, LLC.
51940  *
51941  * Originally Released Under LGPL - original licence link has changed is not relivant.
51942  *
51943  * Fork - LGPL
51944  * <script type="text/javascript">
51945  */
51946
51947 // private - not really -- you end up using it !
51948 // This is a support class used internally by the Grid components
51949
51950 /**
51951  * @class Roo.grid.GridEditor
51952  * @extends Roo.Editor
51953  * Class for creating and editable grid elements.
51954  * @param {Object} config any settings (must include field)
51955  */
51956 Roo.grid.GridEditor = function(field, config){
51957     if (!config && field.field) {
51958         config = field;
51959         field = Roo.factory(config.field, Roo.form);
51960     }
51961     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51962     field.monitorTab = false;
51963 };
51964
51965 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51966     
51967     /**
51968      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51969      */
51970     
51971     alignment: "tl-tl",
51972     autoSize: "width",
51973     hideEl : false,
51974     cls: "x-small-editor x-grid-editor",
51975     shim:false,
51976     shadow:"frame"
51977 });/*
51978  * Based on:
51979  * Ext JS Library 1.1.1
51980  * Copyright(c) 2006-2007, Ext JS, LLC.
51981  *
51982  * Originally Released Under LGPL - original licence link has changed is not relivant.
51983  *
51984  * Fork - LGPL
51985  * <script type="text/javascript">
51986  */
51987   
51988
51989   
51990 Roo.grid.PropertyRecord = Roo.data.Record.create([
51991     {name:'name',type:'string'},  'value'
51992 ]);
51993
51994
51995 Roo.grid.PropertyStore = function(grid, source){
51996     this.grid = grid;
51997     this.store = new Roo.data.Store({
51998         recordType : Roo.grid.PropertyRecord
51999     });
52000     this.store.on('update', this.onUpdate,  this);
52001     if(source){
52002         this.setSource(source);
52003     }
52004     Roo.grid.PropertyStore.superclass.constructor.call(this);
52005 };
52006
52007
52008
52009 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
52010     setSource : function(o){
52011         this.source = o;
52012         this.store.removeAll();
52013         var data = [];
52014         for(var k in o){
52015             if(this.isEditableValue(o[k])){
52016                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
52017             }
52018         }
52019         this.store.loadRecords({records: data}, {}, true);
52020     },
52021
52022     onUpdate : function(ds, record, type){
52023         if(type == Roo.data.Record.EDIT){
52024             var v = record.data['value'];
52025             var oldValue = record.modified['value'];
52026             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
52027                 this.source[record.id] = v;
52028                 record.commit();
52029                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
52030             }else{
52031                 record.reject();
52032             }
52033         }
52034     },
52035
52036     getProperty : function(row){
52037        return this.store.getAt(row);
52038     },
52039
52040     isEditableValue: function(val){
52041         if(val && val instanceof Date){
52042             return true;
52043         }else if(typeof val == 'object' || typeof val == 'function'){
52044             return false;
52045         }
52046         return true;
52047     },
52048
52049     setValue : function(prop, value){
52050         this.source[prop] = value;
52051         this.store.getById(prop).set('value', value);
52052     },
52053
52054     getSource : function(){
52055         return this.source;
52056     }
52057 });
52058
52059 Roo.grid.PropertyColumnModel = function(grid, store){
52060     this.grid = grid;
52061     var g = Roo.grid;
52062     g.PropertyColumnModel.superclass.constructor.call(this, [
52063         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
52064         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
52065     ]);
52066     this.store = store;
52067     this.bselect = Roo.DomHelper.append(document.body, {
52068         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
52069             {tag: 'option', value: 'true', html: 'true'},
52070             {tag: 'option', value: 'false', html: 'false'}
52071         ]
52072     });
52073     Roo.id(this.bselect);
52074     var f = Roo.form;
52075     this.editors = {
52076         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
52077         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
52078         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
52079         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
52080         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
52081     };
52082     this.renderCellDelegate = this.renderCell.createDelegate(this);
52083     this.renderPropDelegate = this.renderProp.createDelegate(this);
52084 };
52085
52086 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
52087     
52088     
52089     nameText : 'Name',
52090     valueText : 'Value',
52091     
52092     dateFormat : 'm/j/Y',
52093     
52094     
52095     renderDate : function(dateVal){
52096         return dateVal.dateFormat(this.dateFormat);
52097     },
52098
52099     renderBool : function(bVal){
52100         return bVal ? 'true' : 'false';
52101     },
52102
52103     isCellEditable : function(colIndex, rowIndex){
52104         return colIndex == 1;
52105     },
52106
52107     getRenderer : function(col){
52108         return col == 1 ?
52109             this.renderCellDelegate : this.renderPropDelegate;
52110     },
52111
52112     renderProp : function(v){
52113         return this.getPropertyName(v);
52114     },
52115
52116     renderCell : function(val){
52117         var rv = val;
52118         if(val instanceof Date){
52119             rv = this.renderDate(val);
52120         }else if(typeof val == 'boolean'){
52121             rv = this.renderBool(val);
52122         }
52123         return Roo.util.Format.htmlEncode(rv);
52124     },
52125
52126     getPropertyName : function(name){
52127         var pn = this.grid.propertyNames;
52128         return pn && pn[name] ? pn[name] : name;
52129     },
52130
52131     getCellEditor : function(colIndex, rowIndex){
52132         var p = this.store.getProperty(rowIndex);
52133         var n = p.data['name'], val = p.data['value'];
52134         
52135         if(typeof(this.grid.customEditors[n]) == 'string'){
52136             return this.editors[this.grid.customEditors[n]];
52137         }
52138         if(typeof(this.grid.customEditors[n]) != 'undefined'){
52139             return this.grid.customEditors[n];
52140         }
52141         if(val instanceof Date){
52142             return this.editors['date'];
52143         }else if(typeof val == 'number'){
52144             return this.editors['number'];
52145         }else if(typeof val == 'boolean'){
52146             return this.editors['boolean'];
52147         }else{
52148             return this.editors['string'];
52149         }
52150     }
52151 });
52152
52153 /**
52154  * @class Roo.grid.PropertyGrid
52155  * @extends Roo.grid.EditorGrid
52156  * This class represents the  interface of a component based property grid control.
52157  * <br><br>Usage:<pre><code>
52158  var grid = new Roo.grid.PropertyGrid("my-container-id", {
52159       
52160  });
52161  // set any options
52162  grid.render();
52163  * </code></pre>
52164   
52165  * @constructor
52166  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52167  * The container MUST have some type of size defined for the grid to fill. The container will be
52168  * automatically set to position relative if it isn't already.
52169  * @param {Object} config A config object that sets properties on this grid.
52170  */
52171 Roo.grid.PropertyGrid = function(container, config){
52172     config = config || {};
52173     var store = new Roo.grid.PropertyStore(this);
52174     this.store = store;
52175     var cm = new Roo.grid.PropertyColumnModel(this, store);
52176     store.store.sort('name', 'ASC');
52177     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
52178         ds: store.store,
52179         cm: cm,
52180         enableColLock:false,
52181         enableColumnMove:false,
52182         stripeRows:false,
52183         trackMouseOver: false,
52184         clicksToEdit:1
52185     }, config));
52186     this.getGridEl().addClass('x-props-grid');
52187     this.lastEditRow = null;
52188     this.on('columnresize', this.onColumnResize, this);
52189     this.addEvents({
52190          /**
52191              * @event beforepropertychange
52192              * Fires before a property changes (return false to stop?)
52193              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52194              * @param {String} id Record Id
52195              * @param {String} newval New Value
52196          * @param {String} oldval Old Value
52197              */
52198         "beforepropertychange": true,
52199         /**
52200              * @event propertychange
52201              * Fires after a property changes
52202              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52203              * @param {String} id Record Id
52204              * @param {String} newval New Value
52205          * @param {String} oldval Old Value
52206              */
52207         "propertychange": true
52208     });
52209     this.customEditors = this.customEditors || {};
52210 };
52211 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52212     
52213      /**
52214      * @cfg {Object} customEditors map of colnames=> custom editors.
52215      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52216      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52217      * false disables editing of the field.
52218          */
52219     
52220       /**
52221      * @cfg {Object} propertyNames map of property Names to their displayed value
52222          */
52223     
52224     render : function(){
52225         Roo.grid.PropertyGrid.superclass.render.call(this);
52226         this.autoSize.defer(100, this);
52227     },
52228
52229     autoSize : function(){
52230         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52231         if(this.view){
52232             this.view.fitColumns();
52233         }
52234     },
52235
52236     onColumnResize : function(){
52237         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52238         this.autoSize();
52239     },
52240     /**
52241      * Sets the data for the Grid
52242      * accepts a Key => Value object of all the elements avaiable.
52243      * @param {Object} data  to appear in grid.
52244      */
52245     setSource : function(source){
52246         this.store.setSource(source);
52247         //this.autoSize();
52248     },
52249     /**
52250      * Gets all the data from the grid.
52251      * @return {Object} data  data stored in grid
52252      */
52253     getSource : function(){
52254         return this.store.getSource();
52255     }
52256 });/*
52257  * Based on:
52258  * Ext JS Library 1.1.1
52259  * Copyright(c) 2006-2007, Ext JS, LLC.
52260  *
52261  * Originally Released Under LGPL - original licence link has changed is not relivant.
52262  *
52263  * Fork - LGPL
52264  * <script type="text/javascript">
52265  */
52266  
52267 /**
52268  * @class Roo.LoadMask
52269  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52270  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52271  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52272  * element's UpdateManager load indicator and will be destroyed after the initial load.
52273  * @constructor
52274  * Create a new LoadMask
52275  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52276  * @param {Object} config The config object
52277  */
52278 Roo.LoadMask = function(el, config){
52279     this.el = Roo.get(el);
52280     Roo.apply(this, config);
52281     if(this.store){
52282         this.store.on('beforeload', this.onBeforeLoad, this);
52283         this.store.on('load', this.onLoad, this);
52284         this.store.on('loadexception', this.onLoadException, this);
52285         this.removeMask = false;
52286     }else{
52287         var um = this.el.getUpdateManager();
52288         um.showLoadIndicator = false; // disable the default indicator
52289         um.on('beforeupdate', this.onBeforeLoad, this);
52290         um.on('update', this.onLoad, this);
52291         um.on('failure', this.onLoad, this);
52292         this.removeMask = true;
52293     }
52294 };
52295
52296 Roo.LoadMask.prototype = {
52297     /**
52298      * @cfg {Boolean} removeMask
52299      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52300      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52301      */
52302     /**
52303      * @cfg {String} msg
52304      * The text to display in a centered loading message box (defaults to 'Loading...')
52305      */
52306     msg : 'Loading...',
52307     /**
52308      * @cfg {String} msgCls
52309      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52310      */
52311     msgCls : 'x-mask-loading',
52312
52313     /**
52314      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52315      * @type Boolean
52316      */
52317     disabled: false,
52318
52319     /**
52320      * Disables the mask to prevent it from being displayed
52321      */
52322     disable : function(){
52323        this.disabled = true;
52324     },
52325
52326     /**
52327      * Enables the mask so that it can be displayed
52328      */
52329     enable : function(){
52330         this.disabled = false;
52331     },
52332     
52333     onLoadException : function()
52334     {
52335         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52336             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52337         }
52338         this.el.unmask(this.removeMask);
52339     },
52340     // private
52341     onLoad : function()
52342     {
52343         this.el.unmask(this.removeMask);
52344     },
52345
52346     // private
52347     onBeforeLoad : function(){
52348         if(!this.disabled){
52349             this.el.mask(this.msg, this.msgCls);
52350         }
52351     },
52352
52353     // private
52354     destroy : function(){
52355         if(this.store){
52356             this.store.un('beforeload', this.onBeforeLoad, this);
52357             this.store.un('load', this.onLoad, this);
52358             this.store.un('loadexception', this.onLoadException, this);
52359         }else{
52360             var um = this.el.getUpdateManager();
52361             um.un('beforeupdate', this.onBeforeLoad, this);
52362             um.un('update', this.onLoad, this);
52363             um.un('failure', this.onLoad, this);
52364         }
52365     }
52366 };/*
52367  * Based on:
52368  * Ext JS Library 1.1.1
52369  * Copyright(c) 2006-2007, Ext JS, LLC.
52370  *
52371  * Originally Released Under LGPL - original licence link has changed is not relivant.
52372  *
52373  * Fork - LGPL
52374  * <script type="text/javascript">
52375  */
52376
52377
52378 /**
52379  * @class Roo.XTemplate
52380  * @extends Roo.Template
52381  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
52382 <pre><code>
52383 var t = new Roo.XTemplate(
52384         '&lt;select name="{name}"&gt;',
52385                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
52386         '&lt;/select&gt;'
52387 );
52388  
52389 // then append, applying the master template values
52390  </code></pre>
52391  *
52392  * Supported features:
52393  *
52394  *  Tags:
52395
52396 <pre><code>
52397       {a_variable} - output encoded.
52398       {a_variable.format:("Y-m-d")} - call a method on the variable
52399       {a_variable:raw} - unencoded output
52400       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
52401       {a_variable:this.method_on_template(...)} - call a method on the template object.
52402  
52403 </code></pre>
52404  *  The tpl tag:
52405 <pre><code>
52406         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
52407         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
52408         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
52409         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
52410   
52411         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
52412         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
52413 </code></pre>
52414  *      
52415  */
52416 Roo.XTemplate = function()
52417 {
52418     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52419     if (this.html) {
52420         this.compile();
52421     }
52422 };
52423
52424
52425 Roo.extend(Roo.XTemplate, Roo.Template, {
52426
52427     /**
52428      * The various sub templates
52429      */
52430     tpls : false,
52431     /**
52432      *
52433      * basic tag replacing syntax
52434      * WORD:WORD()
52435      *
52436      * // you can fake an object call by doing this
52437      *  x.t:(test,tesT) 
52438      * 
52439      */
52440     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52441
52442     /**
52443      * compile the template
52444      *
52445      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
52446      *
52447      */
52448     compile: function()
52449     {
52450         var s = this.html;
52451      
52452         s = ['<tpl>', s, '</tpl>'].join('');
52453     
52454         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
52455             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
52456             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
52457             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
52458             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
52459             m,
52460             id     = 0,
52461             tpls   = [];
52462     
52463         while(true == !!(m = s.match(re))){
52464             var forMatch   = m[0].match(nameRe),
52465                 ifMatch   = m[0].match(ifRe),
52466                 execMatch   = m[0].match(execRe),
52467                 namedMatch   = m[0].match(namedRe),
52468                 
52469                 exp  = null, 
52470                 fn   = null,
52471                 exec = null,
52472                 name = forMatch && forMatch[1] ? forMatch[1] : '';
52473                 
52474             if (ifMatch) {
52475                 // if - puts fn into test..
52476                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
52477                 if(exp){
52478                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52479                 }
52480             }
52481             
52482             if (execMatch) {
52483                 // exec - calls a function... returns empty if true is  returned.
52484                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
52485                 if(exp){
52486                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52487                 }
52488             }
52489             
52490             
52491             if (name) {
52492                 // for = 
52493                 switch(name){
52494                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52495                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52496                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52497                 }
52498             }
52499             var uid = namedMatch ? namedMatch[1] : id;
52500             
52501             
52502             tpls.push({
52503                 id:     namedMatch ? namedMatch[1] : id,
52504                 target: name,
52505                 exec:   exec,
52506                 test:   fn,
52507                 body:   m[1] || ''
52508             });
52509             if (namedMatch) {
52510                 s = s.replace(m[0], '');
52511             } else { 
52512                 s = s.replace(m[0], '{xtpl'+ id + '}');
52513             }
52514             ++id;
52515         }
52516         this.tpls = [];
52517         for(var i = tpls.length-1; i >= 0; --i){
52518             this.compileTpl(tpls[i]);
52519             this.tpls[tpls[i].id] = tpls[i];
52520         }
52521         this.master = tpls[tpls.length-1];
52522         return this;
52523     },
52524     /**
52525      * same as applyTemplate, except it's done to one of the subTemplates
52526      * when using named templates, you can do:
52527      *
52528      * var str = pl.applySubTemplate('your-name', values);
52529      *
52530      * 
52531      * @param {Number} id of the template
52532      * @param {Object} values to apply to template
52533      * @param {Object} parent (normaly the instance of this object)
52534      */
52535     applySubTemplate : function(id, values, parent)
52536     {
52537         
52538         
52539         var t = this.tpls[id];
52540         
52541         
52542         try { 
52543             if(t.test && !t.test.call(this, values, parent)){
52544                 return '';
52545             }
52546         } catch(e) {
52547             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
52548             Roo.log(e.toString());
52549             Roo.log(t.test);
52550             return ''
52551         }
52552         try { 
52553             
52554             if(t.exec && t.exec.call(this, values, parent)){
52555                 return '';
52556             }
52557         } catch(e) {
52558             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
52559             Roo.log(e.toString());
52560             Roo.log(t.exec);
52561             return ''
52562         }
52563         try {
52564             var vs = t.target ? t.target.call(this, values, parent) : values;
52565             parent = t.target ? values : parent;
52566             if(t.target && vs instanceof Array){
52567                 var buf = [];
52568                 for(var i = 0, len = vs.length; i < len; i++){
52569                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
52570                 }
52571                 return buf.join('');
52572             }
52573             return t.compiled.call(this, vs, parent);
52574         } catch (e) {
52575             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
52576             Roo.log(e.toString());
52577             Roo.log(t.compiled);
52578             return '';
52579         }
52580     },
52581
52582     compileTpl : function(tpl)
52583     {
52584         var fm = Roo.util.Format;
52585         var useF = this.disableFormats !== true;
52586         var sep = Roo.isGecko ? "+" : ",";
52587         var undef = function(str) {
52588             Roo.log("Property not found :"  + str);
52589             return '';
52590         };
52591         
52592         var fn = function(m, name, format, args)
52593         {
52594             //Roo.log(arguments);
52595             args = args ? args.replace(/\\'/g,"'") : args;
52596             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
52597             if (typeof(format) == 'undefined') {
52598                 format= 'htmlEncode';
52599             }
52600             if (format == 'raw' ) {
52601                 format = false;
52602             }
52603             
52604             if(name.substr(0, 4) == 'xtpl'){
52605                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52606             }
52607             
52608             // build an array of options to determine if value is undefined..
52609             
52610             // basically get 'xxxx.yyyy' then do
52611             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
52612             //    (function () { Roo.log("Property not found"); return ''; })() :
52613             //    ......
52614             
52615             var udef_ar = [];
52616             var lookfor = '';
52617             Roo.each(name.split('.'), function(st) {
52618                 lookfor += (lookfor.length ? '.': '') + st;
52619                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
52620             });
52621             
52622             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
52623             
52624             
52625             if(format && useF){
52626                 
52627                 args = args ? ',' + args : "";
52628                  
52629                 if(format.substr(0, 5) != "this."){
52630                     format = "fm." + format + '(';
52631                 }else{
52632                     format = 'this.call("'+ format.substr(5) + '", ';
52633                     args = ", values";
52634                 }
52635                 
52636                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
52637             }
52638              
52639             if (args.length) {
52640                 // called with xxyx.yuu:(test,test)
52641                 // change to ()
52642                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
52643             }
52644             // raw.. - :raw modifier..
52645             return "'"+ sep + udef_st  + name + ")"+sep+"'";
52646             
52647         };
52648         var body;
52649         // branched to use + in gecko and [].join() in others
52650         if(Roo.isGecko){
52651             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
52652                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52653                     "';};};";
52654         }else{
52655             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
52656             body.push(tpl.body.replace(/(\r\n|\n)/g,
52657                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52658             body.push("'].join('');};};");
52659             body = body.join('');
52660         }
52661         
52662         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
52663        
52664         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
52665         eval(body);
52666         
52667         return this;
52668     },
52669
52670     applyTemplate : function(values){
52671         return this.master.compiled.call(this, values, {});
52672         //var s = this.subs;
52673     },
52674
52675     apply : function(){
52676         return this.applyTemplate.apply(this, arguments);
52677     }
52678
52679  });
52680
52681 Roo.XTemplate.from = function(el){
52682     el = Roo.getDom(el);
52683     return new Roo.XTemplate(el.value || el.innerHTML);
52684 };/*
52685  * Original code for Roojs - LGPL
52686  * <script type="text/javascript">
52687  */
52688  
52689 /**
52690  * @class Roo.XComponent
52691  * A delayed Element creator...
52692  * Or a way to group chunks of interface together.
52693  * 
52694  * Mypart.xyx = new Roo.XComponent({
52695
52696     parent : 'Mypart.xyz', // empty == document.element.!!
52697     order : '001',
52698     name : 'xxxx'
52699     region : 'xxxx'
52700     disabled : function() {} 
52701      
52702     tree : function() { // return an tree of xtype declared components
52703         var MODULE = this;
52704         return 
52705         {
52706             xtype : 'NestedLayoutPanel',
52707             // technicall
52708         }
52709      ]
52710  *})
52711  *
52712  *
52713  * It can be used to build a big heiracy, with parent etc.
52714  * or you can just use this to render a single compoent to a dom element
52715  * MYPART.render(Roo.Element | String(id) | dom_element )
52716  * 
52717  * @extends Roo.util.Observable
52718  * @constructor
52719  * @param cfg {Object} configuration of component
52720  * 
52721  */
52722 Roo.XComponent = function(cfg) {
52723     Roo.apply(this, cfg);
52724     this.addEvents({ 
52725         /**
52726              * @event built
52727              * Fires when this the componnt is built
52728              * @param {Roo.XComponent} c the component
52729              */
52730         'built' : true
52731         
52732     });
52733     this.region = this.region || 'center'; // default..
52734     Roo.XComponent.register(this);
52735     this.modules = false;
52736     this.el = false; // where the layout goes..
52737     
52738     
52739 }
52740 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52741     /**
52742      * @property el
52743      * The created element (with Roo.factory())
52744      * @type {Roo.Layout}
52745      */
52746     el  : false,
52747     
52748     /**
52749      * @property el
52750      * for BC  - use el in new code
52751      * @type {Roo.Layout}
52752      */
52753     panel : false,
52754     
52755     /**
52756      * @property layout
52757      * for BC  - use el in new code
52758      * @type {Roo.Layout}
52759      */
52760     layout : false,
52761     
52762      /**
52763      * @cfg {Function|boolean} disabled
52764      * If this module is disabled by some rule, return true from the funtion
52765      */
52766     disabled : false,
52767     
52768     /**
52769      * @cfg {String} parent 
52770      * Name of parent element which it get xtype added to..
52771      */
52772     parent: false,
52773     
52774     /**
52775      * @cfg {String} order
52776      * Used to set the order in which elements are created (usefull for multiple tabs)
52777      */
52778     
52779     order : false,
52780     /**
52781      * @cfg {String} name
52782      * String to display while loading.
52783      */
52784     name : false,
52785     /**
52786      * @cfg {String} region
52787      * Region to render component to (defaults to center)
52788      */
52789     region : 'center',
52790     
52791     /**
52792      * @cfg {Array} items
52793      * A single item array - the first element is the root of the tree..
52794      * It's done this way to stay compatible with the Xtype system...
52795      */
52796     items : false,
52797     
52798     /**
52799      * @property _tree
52800      * The method that retuns the tree of parts that make up this compoennt 
52801      * @type {function}
52802      */
52803     _tree  : false,
52804     
52805      /**
52806      * render
52807      * render element to dom or tree
52808      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52809      */
52810     
52811     render : function(el)
52812     {
52813         
52814         el = el || false;
52815         var hp = this.parent ? 1 : 0;
52816         
52817         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52818             // if parent is a '#.....' string, then let's use that..
52819             var ename = this.parent.substr(1)
52820             this.parent = false;
52821             el = Roo.get(ename);
52822             if (!el) {
52823                 Roo.log("Warning - element can not be found :#" + ename );
52824                 return;
52825             }
52826         }
52827         
52828         
52829         if (!this.parent) {
52830             
52831             el = el ? Roo.get(el) : false;      
52832             
52833             // it's a top level one..
52834             this.parent =  {
52835                 el : new Roo.BorderLayout(el || document.body, {
52836                 
52837                      center: {
52838                          titlebar: false,
52839                          autoScroll:false,
52840                          closeOnTab: true,
52841                          tabPosition: 'top',
52842                           //resizeTabs: true,
52843                          alwaysShowTabs: el && hp? false :  true,
52844                          hideTabs: el || !hp ? true :  false,
52845                          minTabWidth: 140
52846                      }
52847                  })
52848             }
52849         }
52850         
52851                 
52852                 // The 'tree' method is  '_tree now' 
52853             
52854         var tree = this._tree ? this._tree() : this.tree();
52855         tree.region = tree.region || this.region;
52856         this.el = this.parent.el.addxtype(tree);
52857         this.fireEvent('built', this);
52858         
52859         this.panel = this.el;
52860         this.layout = this.panel.layout;
52861                 this.parentLayout = this.parent.layout  || false;  
52862          
52863     }
52864     
52865 });
52866
52867 Roo.apply(Roo.XComponent, {
52868     
52869     /**
52870      * @property  buildCompleted
52871      * True when the builder has completed building the interface.
52872      * @type Boolean
52873      */
52874     buildCompleted : false,
52875      
52876     /**
52877      * @property  topModule
52878      * the upper most module - uses document.element as it's constructor.
52879      * @type Object
52880      */
52881      
52882     topModule  : false,
52883       
52884     /**
52885      * @property  modules
52886      * array of modules to be created by registration system.
52887      * @type {Array} of Roo.XComponent
52888      */
52889     
52890     modules : [],
52891     /**
52892      * @property  elmodules
52893      * array of modules to be created by which use #ID 
52894      * @type {Array} of Roo.XComponent
52895      */
52896      
52897     elmodules : [],
52898
52899     
52900     /**
52901      * Register components to be built later.
52902      *
52903      * This solves the following issues
52904      * - Building is not done on page load, but after an authentication process has occured.
52905      * - Interface elements are registered on page load
52906      * - Parent Interface elements may not be loaded before child, so this handles that..
52907      * 
52908      *
52909      * example:
52910      * 
52911      * MyApp.register({
52912           order : '000001',
52913           module : 'Pman.Tab.projectMgr',
52914           region : 'center',
52915           parent : 'Pman.layout',
52916           disabled : false,  // or use a function..
52917         })
52918      
52919      * * @param {Object} details about module
52920      */
52921     register : function(obj) {
52922                 
52923                 Roo.XComponent.event.fireEvent('register', obj);
52924                 switch(typeof(obj.disabled) ) {
52925                         
52926                         case 'undefined':
52927                                 break;
52928                         
52929                         case 'function':
52930                                 if ( obj.disabled() ) {
52931                                         return;
52932                                 }
52933                                 break;
52934                         default:
52935                                 if (obj.disabled) {
52936                                         return;
52937                                 }
52938                                 break;
52939                 }
52940                 
52941         this.modules.push(obj);
52942          
52943     },
52944     /**
52945      * convert a string to an object..
52946      * eg. 'AAA.BBB' -> finds AAA.BBB
52947
52948      */
52949     
52950     toObject : function(str)
52951     {
52952         if (!str || typeof(str) == 'object') {
52953             return str;
52954         }
52955         if (str.substring(0,1) == '#') {
52956             return str;
52957         }
52958
52959         var ar = str.split('.');
52960         var rt, o;
52961         rt = ar.shift();
52962             /** eval:var:o */
52963         try {
52964             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52965         } catch (e) {
52966             throw "Module not found : " + str;
52967         }
52968         
52969         if (o === false) {
52970             throw "Module not found : " + str;
52971         }
52972         Roo.each(ar, function(e) {
52973             if (typeof(o[e]) == 'undefined') {
52974                 throw "Module not found : " + str;
52975             }
52976             o = o[e];
52977         });
52978         
52979         return o;
52980         
52981     },
52982     
52983     
52984     /**
52985      * move modules into their correct place in the tree..
52986      * 
52987      */
52988     preBuild : function ()
52989     {
52990         var _t = this;
52991         Roo.each(this.modules , function (obj)
52992         {
52993             var opar = obj.parent;
52994             try { 
52995                 obj.parent = this.toObject(opar);
52996             } catch(e) {
52997                 Roo.log("parent:toObject failed: " + e.toString());
52998                 return;
52999             }
53000             
53001             if (!obj.parent) {
53002                                 Roo.debug && Roo.log("GOT top level module");
53003                                 Roo.debug && Roo.log(obj);
53004                                 obj.modules = new Roo.util.MixedCollection(false, 
53005                     function(o) { return o.order + '' }
53006                 );
53007                 this.topModule = obj;
53008                 return;
53009             }
53010                         // parent is a string (usually a dom element name..)
53011             if (typeof(obj.parent) == 'string') {
53012                 this.elmodules.push(obj);
53013                 return;
53014             }
53015             if (obj.parent.constructor != Roo.XComponent) {
53016                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
53017             }
53018             if (!obj.parent.modules) {
53019                 obj.parent.modules = new Roo.util.MixedCollection(false, 
53020                     function(o) { return o.order + '' }
53021                 );
53022             }
53023             
53024             obj.parent.modules.add(obj);
53025         }, this);
53026     },
53027     
53028      /**
53029      * make a list of modules to build.
53030      * @return {Array} list of modules. 
53031      */ 
53032     
53033     buildOrder : function()
53034     {
53035         var _this = this;
53036         var cmp = function(a,b) {   
53037             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
53038         };
53039         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
53040             throw "No top level modules to build";
53041         }
53042         
53043         // make a flat list in order of modules to build.
53044         var mods = this.topModule ? [ this.topModule ] : [];
53045                 
53046                 // elmodules (is a list of DOM based modules )
53047         Roo.each(this.elmodules,function(e) { mods.push(e) });
53048
53049         
53050         // add modules to their parents..
53051         var addMod = function(m) {
53052                         Roo.debug && Roo.log("build Order: add: " + m.name);
53053             
53054             mods.push(m);
53055             if (m.modules) {
53056                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
53057                 m.modules.keySort('ASC',  cmp );
53058                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
53059
53060                 m.modules.each(addMod);
53061             } else {
53062                                 Roo.debug && Roo.log("build Order: no child modules");
53063                         }
53064             // not sure if this is used any more..
53065             if (m.finalize) {
53066                 m.finalize.name = m.name + " (clean up) ";
53067                 mods.push(m.finalize);
53068             }
53069             
53070         }
53071         if (this.topModule) { 
53072             this.topModule.modules.keySort('ASC',  cmp );
53073             this.topModule.modules.each(addMod);
53074         }
53075         return mods;
53076     },
53077     
53078      /**
53079      * Build the registered modules.
53080      * @param {Object} parent element.
53081      * @param {Function} optional method to call after module has been added.
53082      * 
53083      */ 
53084    
53085     build : function() 
53086     {
53087         
53088         this.preBuild();
53089         var mods = this.buildOrder();
53090       
53091         //this.allmods = mods;
53092         //Roo.debug && Roo.log(mods);
53093         //return;
53094         if (!mods.length) { // should not happen
53095             throw "NO modules!!!";
53096         }
53097         
53098         
53099         var msg = "Building Interface...";
53100         // flash it up as modal - so we store the mask!?
53101         Roo.MessageBox.show({ title: 'loading' });
53102         Roo.MessageBox.show({
53103            title: "Please wait...",
53104            msg: msg,
53105            width:450,
53106            progress:true,
53107            closable:false,
53108            modal: false
53109           
53110         });
53111         var total = mods.length;
53112         
53113         var _this = this;
53114         var progressRun = function() {
53115             if (!mods.length) {
53116                 Roo.debug && Roo.log('hide?');
53117                 Roo.MessageBox.hide();
53118                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
53119                 
53120                 // THE END...
53121                 return false;   
53122             }
53123             
53124             var m = mods.shift();
53125             
53126             
53127             Roo.debug && Roo.log(m);
53128             // not sure if this is supported any more.. - modules that are are just function
53129             if (typeof(m) == 'function') { 
53130                 m.call(this);
53131                 return progressRun.defer(10, _this);
53132             } 
53133             
53134             
53135             msg = "Building Interface " + (total  - mods.length) + 
53136                     " of " + total + 
53137                     (m.name ? (' - ' + m.name) : '');
53138                         Roo.debug && Roo.log(msg);
53139             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
53140             
53141          
53142             // is the module disabled?
53143             var disabled = (typeof(m.disabled) == 'function') ?
53144                 m.disabled.call(m.module.disabled) : m.disabled;    
53145             
53146             
53147             if (disabled) {
53148                 return progressRun(); // we do not update the display!
53149             }
53150             
53151             // now build 
53152             
53153                         
53154                         
53155             m.render();
53156             // it's 10 on top level, and 1 on others??? why...
53157             return progressRun.defer(10, _this);
53158              
53159         }
53160         progressRun.defer(1, _this);
53161      
53162         
53163         
53164     },
53165         
53166         
53167         /**
53168          * Event Object.
53169          *
53170          *
53171          */
53172         event: false, 
53173     /**
53174          * wrapper for event.on - aliased later..  
53175          * Typically use to register a event handler for register:
53176          *
53177          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
53178          *
53179          */
53180     on : false
53181    
53182     
53183     
53184 });
53185
53186 Roo.XComponent.event = new Roo.util.Observable({
53187                 events : { 
53188                         /**
53189                          * @event register
53190                          * Fires when an Component is registered,
53191                          * set the disable property on the Component to stop registration.
53192                          * @param {Roo.XComponent} c the component being registerd.
53193                          * 
53194                          */
53195                         'register' : true,
53196                         /**
53197                          * @event buildcomplete
53198                          * Fires on the top level element when all elements have been built
53199                          * @param {Roo.XComponent} the top level component.
53200                          */
53201                         'buildcomplete' : true
53202                         
53203                 }
53204 });
53205
53206 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
53207  //<script type="text/javascript">
53208
53209
53210 /**
53211  * @class Roo.Login
53212  * @extends Roo.LayoutDialog
53213  * A generic Login Dialog..... - only one needed in theory!?!?
53214  *
53215  * Fires XComponent builder on success...
53216  * 
53217  * Sends 
53218  *    username,password, lang = for login actions.
53219  *    check = 1 for periodic checking that sesion is valid.
53220  *    passwordRequest = email request password
53221  *    logout = 1 = to logout
53222  * 
53223  * Affects: (this id="????" elements)
53224  *   loading  (removed) (used to indicate application is loading)
53225  *   loading-mask (hides) (used to hide application when it's building loading)
53226  *   
53227  * 
53228  * Usage: 
53229  *    
53230  * 
53231  * Myapp.login = Roo.Login({
53232      url: xxxx,
53233    
53234      realm : 'Myapp', 
53235      
53236      
53237      method : 'POST',
53238      
53239      
53240      * 
53241  })
53242  * 
53243  * 
53244  * 
53245  **/
53246  
53247 Roo.Login = function(cfg)
53248 {
53249     this.addEvents({
53250         'refreshed' : true
53251     });
53252     
53253     Roo.apply(this,cfg);
53254     
53255     Roo.onReady(function() {
53256         this.onLoad();
53257     }, this);
53258     // call parent..
53259     
53260    
53261     Roo.Login.superclass.constructor.call(this, this);
53262     //this.addxtype(this.items[0]);
53263     
53264     
53265 }
53266
53267
53268 Roo.extend(Roo.Login, Roo.LayoutDialog, {
53269     
53270     /**
53271      * @cfg {String} method
53272      * Method used to query for login details.
53273      */
53274     
53275     method : 'POST',
53276     /**
53277      * @cfg {String} url
53278      * URL to query login data. - eg. baseURL + '/Login.php'
53279      */
53280     url : '',
53281     
53282     /**
53283      * @property user
53284      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
53285      * @type {Object} 
53286      */
53287     user : false,
53288     /**
53289      * @property checkFails
53290      * Number of times we have attempted to get authentication check, and failed.
53291      * @type {Number} 
53292      */
53293     checkFails : 0,
53294       /**
53295      * @property intervalID
53296      * The window interval that does the constant login checking.
53297      * @type {Number} 
53298      */
53299     intervalID : 0,
53300     
53301     
53302     onLoad : function() // called on page load...
53303     {
53304         // load 
53305          
53306         if (Roo.get('loading')) { // clear any loading indicator..
53307             Roo.get('loading').remove();
53308         }
53309         
53310         //this.switchLang('en'); // set the language to english..
53311        
53312         this.check({
53313             success:  function(response, opts)  {  // check successfull...
53314             
53315                 var res = this.processResponse(response);
53316                 this.checkFails =0;
53317                 if (!res.success) { // error!
53318                     this.checkFails = 5;
53319                     //console.log('call failure');
53320                     return this.failure(response,opts);
53321                 }
53322                 
53323                 if (!res.data.id) { // id=0 == login failure.
53324                     return this.show();
53325                 }
53326                 
53327                               
53328                         //console.log(success);
53329                 this.fillAuth(res.data);   
53330                 this.checkFails =0;
53331                 Roo.XComponent.build();
53332             },
53333             failure : this.show
53334         });
53335         
53336     }, 
53337     
53338     
53339     check: function(cfg) // called every so often to refresh cookie etc..
53340     {
53341         if (cfg.again) { // could be undefined..
53342             this.checkFails++;
53343         } else {
53344             this.checkFails = 0;
53345         }
53346         var _this = this;
53347         if (this.sending) {
53348             if ( this.checkFails > 4) {
53349                 Roo.MessageBox.alert("Error",  
53350                     "Error getting authentication status. - try reloading, or wait a while", function() {
53351                         _this.sending = false;
53352                     }); 
53353                 return;
53354             }
53355             cfg.again = true;
53356             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
53357             return;
53358         }
53359         this.sending = true;
53360         
53361         Roo.Ajax.request({  
53362             url: this.url,
53363             params: {
53364                 getAuthUser: true
53365             },  
53366             method: this.method,
53367             success:  cfg.success || this.success,
53368             failure : cfg.failure || this.failure,
53369             scope : this,
53370             callCfg : cfg
53371               
53372         });  
53373     }, 
53374     
53375     
53376     logout: function()
53377     {
53378         window.onbeforeunload = function() { }; // false does not work for IE..
53379         this.user = false;
53380         var _this = this;
53381         
53382         Roo.Ajax.request({  
53383             url: this.url,
53384             params: {
53385                 logout: 1
53386             },  
53387             method: 'GET',
53388             failure : function() {
53389                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
53390                     document.location = document.location.toString() + '?ts=' + Math.random();
53391                 });
53392                 
53393             },
53394             success : function() {
53395                 _this.user = false;
53396                 this.checkFails =0;
53397                 // fixme..
53398                 document.location = document.location.toString() + '?ts=' + Math.random();
53399             }
53400               
53401               
53402         }); 
53403     },
53404     
53405     processResponse : function (response)
53406     {
53407         var res = '';
53408         try {
53409             res = Roo.decode(response.responseText);
53410             // oops...
53411             if (typeof(res) != 'object') {
53412                 res = { success : false, errorMsg : res, errors : true };
53413             }
53414             if (typeof(res.success) == 'undefined') {
53415                 res.success = false;
53416             }
53417             
53418         } catch(e) {
53419             res = { success : false,  errorMsg : response.responseText, errors : true };
53420         }
53421         return res;
53422     },
53423     
53424     success : function(response, opts)  // check successfull...
53425     {  
53426         this.sending = false;
53427         var res = this.processResponse(response);
53428         if (!res.success) {
53429             return this.failure(response, opts);
53430         }
53431         if (!res.data || !res.data.id) {
53432             return this.failure(response,opts);
53433         }
53434         //console.log(res);
53435         this.fillAuth(res.data);
53436         
53437         this.checkFails =0;
53438         
53439     },
53440     
53441     
53442     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53443     {
53444         this.authUser = -1;
53445         this.sending = false;
53446         var res = this.processResponse(response);
53447         //console.log(res);
53448         if ( this.checkFails > 2) {
53449         
53450             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53451                 "Error getting authentication status. - try reloading"); 
53452             return;
53453         }
53454         opts.callCfg.again = true;
53455         this.check.defer(1000, this, [ opts.callCfg ]);
53456         return;  
53457     },
53458     
53459     
53460     
53461     fillAuth: function(au) {
53462         this.startAuthCheck();
53463         this.authUserId = au.id;
53464         this.authUser = au;
53465         this.lastChecked = new Date();
53466         this.fireEvent('refreshed', au);
53467         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53468         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53469         au.lang = au.lang || 'en';
53470         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53471         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53472         this.switchLang(au.lang );
53473         
53474      
53475         // open system... - -on setyp..
53476         if (this.authUserId  < 0) {
53477             Roo.MessageBox.alert("Warning", 
53478                 "This is an open system - please set up a admin user with a password.");  
53479         }
53480          
53481         //Pman.onload(); // which should do nothing if it's a re-auth result...
53482         
53483              
53484     },
53485     
53486     startAuthCheck : function() // starter for timeout checking..
53487     {
53488         if (this.intervalID) { // timer already in place...
53489             return false;
53490         }
53491         var _this = this;
53492         this.intervalID =  window.setInterval(function() {
53493               _this.check(false);
53494             }, 120000); // every 120 secs = 2mins..
53495         
53496         
53497     },
53498          
53499     
53500     switchLang : function (lang) 
53501     {
53502         _T = typeof(_T) == 'undefined' ? false : _T;
53503           if (!_T || !lang.length) {
53504             return;
53505         }
53506         
53507         if (!_T && lang != 'en') {
53508             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53509             return;
53510         }
53511         
53512         if (typeof(_T.en) == 'undefined') {
53513             _T.en = {};
53514             Roo.apply(_T.en, _T);
53515         }
53516         
53517         if (typeof(_T[lang]) == 'undefined') {
53518             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53519             return;
53520         }
53521         
53522         
53523         Roo.apply(_T, _T[lang]);
53524         // just need to set the text values for everything...
53525         var _this = this;
53526         /* this will not work ...
53527         if (this.form) { 
53528             
53529                
53530             function formLabel(name, val) {
53531                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53532             }
53533             
53534             formLabel('password', "Password"+':');
53535             formLabel('username', "Email Address"+':');
53536             formLabel('lang', "Language"+':');
53537             this.dialog.setTitle("Login");
53538             this.dialog.buttons[0].setText("Forgot Password");
53539             this.dialog.buttons[1].setText("Login");
53540         }
53541         */
53542         
53543         
53544     },
53545     
53546     
53547     title: "Login",
53548     modal: true,
53549     width:  350,
53550     //height: 230,
53551     height: 180,
53552     shadow: true,
53553     minWidth:200,
53554     minHeight:180,
53555     //proxyDrag: true,
53556     closable: false,
53557     draggable: false,
53558     collapsible: false,
53559     resizable: false,
53560     center: {  // needed??
53561         autoScroll:false,
53562         titlebar: false,
53563        // tabPosition: 'top',
53564         hideTabs: true,
53565         closeOnTab: true,
53566         alwaysShowTabs: false
53567     } ,
53568     listeners : {
53569         
53570         show  : function(dlg)
53571         {
53572             //console.log(this);
53573             this.form = this.layout.getRegion('center').activePanel.form;
53574             this.form.dialog = dlg;
53575             this.buttons[0].form = this.form;
53576             this.buttons[0].dialog = dlg;
53577             this.buttons[1].form = this.form;
53578             this.buttons[1].dialog = dlg;
53579            
53580            //this.resizeToLogo.defer(1000,this);
53581             // this is all related to resizing for logos..
53582             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53583            //// if (!sz) {
53584              //   this.resizeToLogo.defer(1000,this);
53585              //   return;
53586            // }
53587             //var w = Ext.lib.Dom.getViewWidth() - 100;
53588             //var h = Ext.lib.Dom.getViewHeight() - 100;
53589             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53590             //this.center();
53591             if (this.disabled) {
53592                 this.hide();
53593                 return;
53594             }
53595             
53596             if (this.user.id < 0) { // used for inital setup situations.
53597                 return;
53598             }
53599             
53600             if (this.intervalID) {
53601                 // remove the timer
53602                 window.clearInterval(this.intervalID);
53603                 this.intervalID = false;
53604             }
53605             
53606             
53607             if (Roo.get('loading')) {
53608                 Roo.get('loading').remove();
53609             }
53610             if (Roo.get('loading-mask')) {
53611                 Roo.get('loading-mask').hide();
53612             }
53613             
53614             //incomming._node = tnode;
53615             this.form.reset();
53616             //this.dialog.modal = !modal;
53617             //this.dialog.show();
53618             this.el.unmask(); 
53619             
53620             
53621             this.form.setValues({
53622                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53623                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53624             });
53625             
53626             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53627             if (this.form.findField('username').getValue().length > 0 ){
53628                 this.form.findField('password').focus();
53629             } else {
53630                this.form.findField('username').focus();
53631             }
53632     
53633         }
53634     },
53635     items : [
53636          {
53637        
53638             xtype : 'ContentPanel',
53639             xns : Roo,
53640             region: 'center',
53641             fitToFrame : true,
53642             
53643             items : [
53644     
53645                 {
53646                
53647                     xtype : 'Form',
53648                     xns : Roo.form,
53649                     labelWidth: 100,
53650                     style : 'margin: 10px;',
53651                     
53652                     listeners : {
53653                         actionfailed : function(f, act) {
53654                             // form can return { errors: .... }
53655                                 
53656                             //act.result.errors // invalid form element list...
53657                             //act.result.errorMsg// invalid form element list...
53658                             
53659                             this.dialog.el.unmask();
53660                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53661                                         "Login failed - communication error - try again.");
53662                                       
53663                         },
53664                         actioncomplete: function(re, act) {
53665                              
53666                             Roo.state.Manager.set(
53667                                 this.dialog.realm + '.username',  
53668                                     this.findField('username').getValue()
53669                             );
53670                             Roo.state.Manager.set(
53671                                 this.dialog.realm + '.lang',  
53672                                 this.findField('lang').getValue() 
53673                             );
53674                             
53675                             this.dialog.fillAuth(act.result.data);
53676                               
53677                             this.dialog.hide();
53678                             
53679                             if (Roo.get('loading-mask')) {
53680                                 Roo.get('loading-mask').show();
53681                             }
53682                             Roo.XComponent.build();
53683                             
53684                              
53685                             
53686                         }
53687                     },
53688                     items : [
53689                         {
53690                             xtype : 'TextField',
53691                             xns : Roo.form,
53692                             fieldLabel: "Email Address",
53693                             name: 'username',
53694                             width:200,
53695                             autoCreate : {tag: "input", type: "text", size: "20"}
53696                         },
53697                         {
53698                             xtype : 'TextField',
53699                             xns : Roo.form,
53700                             fieldLabel: "Password",
53701                             inputType: 'password',
53702                             name: 'password',
53703                             width:200,
53704                             autoCreate : {tag: "input", type: "text", size: "20"},
53705                             listeners : {
53706                                 specialkey : function(e,ev) {
53707                                     if (ev.keyCode == 13) {
53708                                         this.form.dialog.el.mask("Logging in");
53709                                         this.form.doAction('submit', {
53710                                             url: this.form.dialog.url,
53711                                             method: this.form.dialog.method
53712                                         });
53713                                     }
53714                                 }
53715                             }  
53716                         },
53717                         {
53718                             xtype : 'ComboBox',
53719                             xns : Roo.form,
53720                             fieldLabel: "Language",
53721                             name : 'langdisp',
53722                             store: {
53723                                 xtype : 'SimpleStore',
53724                                 fields: ['lang', 'ldisp'],
53725                                 data : [
53726                                     [ 'en', 'English' ],
53727                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53728                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53729                                 ]
53730                             },
53731                             
53732                             valueField : 'lang',
53733                             hiddenName:  'lang',
53734                             width: 200,
53735                             displayField:'ldisp',
53736                             typeAhead: false,
53737                             editable: false,
53738                             mode: 'local',
53739                             triggerAction: 'all',
53740                             emptyText:'Select a Language...',
53741                             selectOnFocus:true,
53742                             listeners : {
53743                                 select :  function(cb, rec, ix) {
53744                                     this.form.switchLang(rec.data.lang);
53745                                 }
53746                             }
53747                         
53748                         }
53749                     ]
53750                 }
53751                   
53752                 
53753             ]
53754         }
53755     ],
53756     buttons : [
53757         {
53758             xtype : 'Button',
53759             xns : 'Roo',
53760             text : "Forgot Password",
53761             listeners : {
53762                 click : function() {
53763                     //console.log(this);
53764                     var n = this.form.findField('username').getValue();
53765                     if (!n.length) {
53766                         Roo.MessageBox.alert("Error", "Fill in your email address");
53767                         return;
53768                     }
53769                     Roo.Ajax.request({
53770                         url: this.dialog.url,
53771                         params: {
53772                             passwordRequest: n
53773                         },
53774                         method: this.dialog.method,
53775                         success:  function(response, opts)  {  // check successfull...
53776                         
53777                             var res = this.dialog.processResponse(response);
53778                             if (!res.success) { // error!
53779                                Roo.MessageBox.alert("Error" ,
53780                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53781                                return;
53782                             }
53783                             Roo.MessageBox.alert("Notice" ,
53784                                 "Please check you email for the Password Reset message");
53785                         },
53786                         failure : function() {
53787                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53788                         }
53789                         
53790                     });
53791                 }
53792             }
53793         },
53794         {
53795             xtype : 'Button',
53796             xns : 'Roo',
53797             text : "Login",
53798             listeners : {
53799                 
53800                 click : function () {
53801                         
53802                     this.dialog.el.mask("Logging in");
53803                     this.form.doAction('submit', {
53804                             url: this.dialog.url,
53805                             method: this.dialog.method
53806                     });
53807                 }
53808             }
53809         }
53810     ]
53811   
53812   
53813 })
53814  
53815
53816
53817