roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 Roo.lib.Dom = {
1778     getViewWidth : function(full) {
1779         return full ? this.getDocumentWidth() : this.getViewportWidth();
1780     },
1781
1782     getViewHeight : function(full) {
1783         return full ? this.getDocumentHeight() : this.getViewportHeight();
1784     },
1785
1786     getDocumentHeight: function() {
1787         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1788         return Math.max(scrollHeight, this.getViewportHeight());
1789     },
1790
1791     getDocumentWidth: function() {
1792         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1793         return Math.max(scrollWidth, this.getViewportWidth());
1794     },
1795
1796     getViewportHeight: function() {
1797         var height = self.innerHeight;
1798         var mode = document.compatMode;
1799
1800         if ((mode || Roo.isIE) && !Roo.isOpera) {
1801             height = (mode == "CSS1Compat") ?
1802                      document.documentElement.clientHeight :
1803                      document.body.clientHeight;
1804         }
1805
1806         return height;
1807     },
1808
1809     getViewportWidth: function() {
1810         var width = self.innerWidth;
1811         var mode = document.compatMode;
1812
1813         if (mode || Roo.isIE) {
1814             width = (mode == "CSS1Compat") ?
1815                     document.documentElement.clientWidth :
1816                     document.body.clientWidth;
1817         }
1818         return width;
1819     },
1820
1821     isAncestor : function(p, c) {
1822         p = Roo.getDom(p);
1823         c = Roo.getDom(c);
1824         if (!p || !c) {
1825             return false;
1826         }
1827
1828         if (p.contains && !Roo.isSafari) {
1829             return p.contains(c);
1830         } else if (p.compareDocumentPosition) {
1831             return !!(p.compareDocumentPosition(c) & 16);
1832         } else {
1833             var parent = c.parentNode;
1834             while (parent) {
1835                 if (parent == p) {
1836                     return true;
1837                 }
1838                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1839                     return false;
1840                 }
1841                 parent = parent.parentNode;
1842             }
1843             return false;
1844         }
1845     },
1846
1847     getRegion : function(el) {
1848         return Roo.lib.Region.getRegion(el);
1849     },
1850
1851     getY : function(el) {
1852         return this.getXY(el)[1];
1853     },
1854
1855     getX : function(el) {
1856         return this.getXY(el)[0];
1857     },
1858
1859     getXY : function(el) {
1860         var p, pe, b, scroll, bd = document.body;
1861         el = Roo.getDom(el);
1862         var fly = Roo.lib.AnimBase.fly;
1863         if (el.getBoundingClientRect) {
1864             b = el.getBoundingClientRect();
1865             scroll = fly(document).getScroll();
1866             return [b.left + scroll.left, b.top + scroll.top];
1867         }
1868         var x = 0, y = 0;
1869
1870         p = el;
1871
1872         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1873
1874         while (p) {
1875
1876             x += p.offsetLeft;
1877             y += p.offsetTop;
1878
1879             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1880                 hasAbsolute = true;
1881             }
1882
1883             if (Roo.isGecko) {
1884                 pe = fly(p);
1885
1886                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1887                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1888
1889
1890                 x += bl;
1891                 y += bt;
1892
1893
1894                 if (p != el && pe.getStyle('overflow') != 'visible') {
1895                     x += bl;
1896                     y += bt;
1897                 }
1898             }
1899             p = p.offsetParent;
1900         }
1901
1902         if (Roo.isSafari && hasAbsolute) {
1903             x -= bd.offsetLeft;
1904             y -= bd.offsetTop;
1905         }
1906
1907         if (Roo.isGecko && !hasAbsolute) {
1908             var dbd = fly(bd);
1909             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1910             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1911         }
1912
1913         p = el.parentNode;
1914         while (p && p != bd) {
1915             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1916                 x -= p.scrollLeft;
1917                 y -= p.scrollTop;
1918             }
1919             p = p.parentNode;
1920         }
1921         return [x, y];
1922     },
1923  
1924   
1925
1926
1927     setXY : function(el, xy) {
1928         el = Roo.fly(el, '_setXY');
1929         el.position();
1930         var pts = el.translatePoints(xy);
1931         if (xy[0] !== false) {
1932             el.dom.style.left = pts.left + "px";
1933         }
1934         if (xy[1] !== false) {
1935             el.dom.style.top = pts.top + "px";
1936         }
1937     },
1938
1939     setX : function(el, x) {
1940         this.setXY(el, [x, false]);
1941     },
1942
1943     setY : function(el, y) {
1944         this.setXY(el, [false, y]);
1945     }
1946 };
1947 /*
1948  * Portions of this file are based on pieces of Yahoo User Interface Library
1949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1950  * YUI licensed under the BSD License:
1951  * http://developer.yahoo.net/yui/license.txt
1952  * <script type="text/javascript">
1953  *
1954  */
1955
1956 Roo.lib.Event = function() {
1957     var loadComplete = false;
1958     var listeners = [];
1959     var unloadListeners = [];
1960     var retryCount = 0;
1961     var onAvailStack = [];
1962     var counter = 0;
1963     var lastError = null;
1964
1965     return {
1966         POLL_RETRYS: 200,
1967         POLL_INTERVAL: 20,
1968         EL: 0,
1969         TYPE: 1,
1970         FN: 2,
1971         WFN: 3,
1972         OBJ: 3,
1973         ADJ_SCOPE: 4,
1974         _interval: null,
1975
1976         startInterval: function() {
1977             if (!this._interval) {
1978                 var self = this;
1979                 var callback = function() {
1980                     self._tryPreloadAttach();
1981                 };
1982                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1983
1984             }
1985         },
1986
1987         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1988             onAvailStack.push({ id:         p_id,
1989                 fn:         p_fn,
1990                 obj:        p_obj,
1991                 override:   p_override,
1992                 checkReady: false    });
1993
1994             retryCount = this.POLL_RETRYS;
1995             this.startInterval();
1996         },
1997
1998
1999         addListener: function(el, eventName, fn) {
2000             el = Roo.getDom(el);
2001             if (!el || !fn) {
2002                 return false;
2003             }
2004
2005             if ("unload" == eventName) {
2006                 unloadListeners[unloadListeners.length] =
2007                 [el, eventName, fn];
2008                 return true;
2009             }
2010
2011             var wrappedFn = function(e) {
2012                 return fn(Roo.lib.Event.getEvent(e));
2013             };
2014
2015             var li = [el, eventName, fn, wrappedFn];
2016
2017             var index = listeners.length;
2018             listeners[index] = li;
2019
2020             this.doAdd(el, eventName, wrappedFn, false);
2021             return true;
2022
2023         },
2024
2025
2026         removeListener: function(el, eventName, fn) {
2027             var i, len;
2028
2029             el = Roo.getDom(el);
2030
2031             if(!fn) {
2032                 return this.purgeElement(el, false, eventName);
2033             }
2034
2035
2036             if ("unload" == eventName) {
2037
2038                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2039                     var li = unloadListeners[i];
2040                     if (li &&
2041                         li[0] == el &&
2042                         li[1] == eventName &&
2043                         li[2] == fn) {
2044                         unloadListeners.splice(i, 1);
2045                         return true;
2046                     }
2047                 }
2048
2049                 return false;
2050             }
2051
2052             var cacheItem = null;
2053
2054
2055             var index = arguments[3];
2056
2057             if ("undefined" == typeof index) {
2058                 index = this._getCacheIndex(el, eventName, fn);
2059             }
2060
2061             if (index >= 0) {
2062                 cacheItem = listeners[index];
2063             }
2064
2065             if (!el || !cacheItem) {
2066                 return false;
2067             }
2068
2069             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2070
2071             delete listeners[index][this.WFN];
2072             delete listeners[index][this.FN];
2073             listeners.splice(index, 1);
2074
2075             return true;
2076
2077         },
2078
2079
2080         getTarget: function(ev, resolveTextNode) {
2081             ev = ev.browserEvent || ev;
2082             var t = ev.target || ev.srcElement;
2083             return this.resolveTextNode(t);
2084         },
2085
2086
2087         resolveTextNode: function(node) {
2088             if (Roo.isSafari && node && 3 == node.nodeType) {
2089                 return node.parentNode;
2090             } else {
2091                 return node;
2092             }
2093         },
2094
2095
2096         getPageX: function(ev) {
2097             ev = ev.browserEvent || ev;
2098             var x = ev.pageX;
2099             if (!x && 0 !== x) {
2100                 x = ev.clientX || 0;
2101
2102                 if (Roo.isIE) {
2103                     x += this.getScroll()[1];
2104                 }
2105             }
2106
2107             return x;
2108         },
2109
2110
2111         getPageY: function(ev) {
2112             ev = ev.browserEvent || ev;
2113             var y = ev.pageY;
2114             if (!y && 0 !== y) {
2115                 y = ev.clientY || 0;
2116
2117                 if (Roo.isIE) {
2118                     y += this.getScroll()[0];
2119                 }
2120             }
2121
2122
2123             return y;
2124         },
2125
2126
2127         getXY: function(ev) {
2128             ev = ev.browserEvent || ev;
2129             return [this.getPageX(ev), this.getPageY(ev)];
2130         },
2131
2132
2133         getRelatedTarget: function(ev) {
2134             ev = ev.browserEvent || ev;
2135             var t = ev.relatedTarget;
2136             if (!t) {
2137                 if (ev.type == "mouseout") {
2138                     t = ev.toElement;
2139                 } else if (ev.type == "mouseover") {
2140                     t = ev.fromElement;
2141                 }
2142             }
2143
2144             return this.resolveTextNode(t);
2145         },
2146
2147
2148         getTime: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             if (!ev.time) {
2151                 var t = new Date().getTime();
2152                 try {
2153                     ev.time = t;
2154                 } catch(ex) {
2155                     this.lastError = ex;
2156                     return t;
2157                 }
2158             }
2159
2160             return ev.time;
2161         },
2162
2163
2164         stopEvent: function(ev) {
2165             this.stopPropagation(ev);
2166             this.preventDefault(ev);
2167         },
2168
2169
2170         stopPropagation: function(ev) {
2171             ev = ev.browserEvent || ev;
2172             if (ev.stopPropagation) {
2173                 ev.stopPropagation();
2174             } else {
2175                 ev.cancelBubble = true;
2176             }
2177         },
2178
2179
2180         preventDefault: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             if(ev.preventDefault) {
2183                 ev.preventDefault();
2184             } else {
2185                 ev.returnValue = false;
2186             }
2187         },
2188
2189
2190         getEvent: function(e) {
2191             var ev = e || window.event;
2192             if (!ev) {
2193                 var c = this.getEvent.caller;
2194                 while (c) {
2195                     ev = c.arguments[0];
2196                     if (ev && Event == ev.constructor) {
2197                         break;
2198                     }
2199                     c = c.caller;
2200                 }
2201             }
2202             return ev;
2203         },
2204
2205
2206         getCharCode: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             return ev.charCode || ev.keyCode || 0;
2209         },
2210
2211
2212         _getCacheIndex: function(el, eventName, fn) {
2213             for (var i = 0,len = listeners.length; i < len; ++i) {
2214                 var li = listeners[i];
2215                 if (li &&
2216                     li[this.FN] == fn &&
2217                     li[this.EL] == el &&
2218                     li[this.TYPE] == eventName) {
2219                     return i;
2220                 }
2221             }
2222
2223             return -1;
2224         },
2225
2226
2227         elCache: {},
2228
2229
2230         getEl: function(id) {
2231             return document.getElementById(id);
2232         },
2233
2234
2235         clearCache: function() {
2236         },
2237
2238
2239         _load: function(e) {
2240             loadComplete = true;
2241             var EU = Roo.lib.Event;
2242
2243
2244             if (Roo.isIE) {
2245                 EU.doRemove(window, "load", EU._load);
2246             }
2247         },
2248
2249
2250         _tryPreloadAttach: function() {
2251
2252             if (this.locked) {
2253                 return false;
2254             }
2255
2256             this.locked = true;
2257
2258
2259             var tryAgain = !loadComplete;
2260             if (!tryAgain) {
2261                 tryAgain = (retryCount > 0);
2262             }
2263
2264
2265             var notAvail = [];
2266             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2267                 var item = onAvailStack[i];
2268                 if (item) {
2269                     var el = this.getEl(item.id);
2270
2271                     if (el) {
2272                         if (!item.checkReady ||
2273                             loadComplete ||
2274                             el.nextSibling ||
2275                             (document && document.body)) {
2276
2277                             var scope = el;
2278                             if (item.override) {
2279                                 if (item.override === true) {
2280                                     scope = item.obj;
2281                                 } else {
2282                                     scope = item.override;
2283                                 }
2284                             }
2285                             item.fn.call(scope, item.obj);
2286                             onAvailStack[i] = null;
2287                         }
2288                     } else {
2289                         notAvail.push(item);
2290                     }
2291                 }
2292             }
2293
2294             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2295
2296             if (tryAgain) {
2297
2298                 this.startInterval();
2299             } else {
2300                 clearInterval(this._interval);
2301                 this._interval = null;
2302             }
2303
2304             this.locked = false;
2305
2306             return true;
2307
2308         },
2309
2310
2311         purgeElement: function(el, recurse, eventName) {
2312             var elListeners = this.getListeners(el, eventName);
2313             if (elListeners) {
2314                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2315                     var l = elListeners[i];
2316                     this.removeListener(el, l.type, l.fn);
2317                 }
2318             }
2319
2320             if (recurse && el && el.childNodes) {
2321                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2322                     this.purgeElement(el.childNodes[i], recurse, eventName);
2323                 }
2324             }
2325         },
2326
2327
2328         getListeners: function(el, eventName) {
2329             var results = [], searchLists;
2330             if (!eventName) {
2331                 searchLists = [listeners, unloadListeners];
2332             } else if (eventName == "unload") {
2333                 searchLists = [unloadListeners];
2334             } else {
2335                 searchLists = [listeners];
2336             }
2337
2338             for (var j = 0; j < searchLists.length; ++j) {
2339                 var searchList = searchLists[j];
2340                 if (searchList && searchList.length > 0) {
2341                     for (var i = 0,len = searchList.length; i < len; ++i) {
2342                         var l = searchList[i];
2343                         if (l && l[this.EL] === el &&
2344                             (!eventName || eventName === l[this.TYPE])) {
2345                             results.push({
2346                                 type:   l[this.TYPE],
2347                                 fn:     l[this.FN],
2348                                 obj:    l[this.OBJ],
2349                                 adjust: l[this.ADJ_SCOPE],
2350                                 index:  i
2351                             });
2352                         }
2353                     }
2354                 }
2355             }
2356
2357             return (results.length) ? results : null;
2358         },
2359
2360
2361         _unload: function(e) {
2362
2363             var EU = Roo.lib.Event, i, j, l, len, index;
2364
2365             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2366                 l = unloadListeners[i];
2367                 if (l) {
2368                     var scope = window;
2369                     if (l[EU.ADJ_SCOPE]) {
2370                         if (l[EU.ADJ_SCOPE] === true) {
2371                             scope = l[EU.OBJ];
2372                         } else {
2373                             scope = l[EU.ADJ_SCOPE];
2374                         }
2375                     }
2376                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2377                     unloadListeners[i] = null;
2378                     l = null;
2379                     scope = null;
2380                 }
2381             }
2382
2383             unloadListeners = null;
2384
2385             if (listeners && listeners.length > 0) {
2386                 j = listeners.length;
2387                 while (j) {
2388                     index = j - 1;
2389                     l = listeners[index];
2390                     if (l) {
2391                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2392                                 l[EU.FN], index);
2393                     }
2394                     j = j - 1;
2395                 }
2396                 l = null;
2397
2398                 EU.clearCache();
2399             }
2400
2401             EU.doRemove(window, "unload", EU._unload);
2402
2403         },
2404
2405
2406         getScroll: function() {
2407             var dd = document.documentElement, db = document.body;
2408             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2409                 return [dd.scrollTop, dd.scrollLeft];
2410             } else if (db) {
2411                 return [db.scrollTop, db.scrollLeft];
2412             } else {
2413                 return [0, 0];
2414             }
2415         },
2416
2417
2418         doAdd: function () {
2419             if (window.addEventListener) {
2420                 return function(el, eventName, fn, capture) {
2421                     el.addEventListener(eventName, fn, (capture));
2422                 };
2423             } else if (window.attachEvent) {
2424                 return function(el, eventName, fn, capture) {
2425                     el.attachEvent("on" + eventName, fn);
2426                 };
2427             } else {
2428                 return function() {
2429                 };
2430             }
2431         }(),
2432
2433
2434         doRemove: function() {
2435             if (window.removeEventListener) {
2436                 return function (el, eventName, fn, capture) {
2437                     el.removeEventListener(eventName, fn, (capture));
2438                 };
2439             } else if (window.detachEvent) {
2440                 return function (el, eventName, fn) {
2441                     el.detachEvent("on" + eventName, fn);
2442                 };
2443             } else {
2444                 return function() {
2445                 };
2446             }
2447         }()
2448     };
2449     
2450 }();
2451 (function() {     
2452    
2453     var E = Roo.lib.Event;
2454     E.on = E.addListener;
2455     E.un = E.removeListener;
2456
2457     if (document && document.body) {
2458         E._load();
2459     } else {
2460         E.doAdd(window, "load", E._load);
2461     }
2462     E.doAdd(window, "unload", E._unload);
2463     E._tryPreloadAttach();
2464 })();
2465
2466 /*
2467  * Portions of this file are based on pieces of Yahoo User Interface Library
2468  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2469  * YUI licensed under the BSD License:
2470  * http://developer.yahoo.net/yui/license.txt
2471  * <script type="text/javascript">
2472  *
2473  */
2474
2475 (function() {
2476     /**
2477      * @class Roo.lib.Ajax
2478      *
2479      */
2480     Roo.lib.Ajax = {
2481         /**
2482          * @static 
2483          */
2484         request : function(method, uri, cb, data, options) {
2485             if(options){
2486                 var hs = options.headers;
2487                 if(hs){
2488                     for(var h in hs){
2489                         if(hs.hasOwnProperty(h)){
2490                             this.initHeader(h, hs[h], false);
2491                         }
2492                     }
2493                 }
2494                 if(options.xmlData){
2495                     this.initHeader('Content-Type', 'text/xml', false);
2496                     method = 'POST';
2497                     data = options.xmlData;
2498                 }
2499             }
2500
2501             return this.asyncRequest(method, uri, cb, data);
2502         },
2503
2504         serializeForm : function(form) {
2505             if(typeof form == 'string') {
2506                 form = (document.getElementById(form) || document.forms[form]);
2507             }
2508
2509             var el, name, val, disabled, data = '', hasSubmit = false;
2510             for (var i = 0; i < form.elements.length; i++) {
2511                 el = form.elements[i];
2512                 disabled = form.elements[i].disabled;
2513                 name = form.elements[i].name;
2514                 val = form.elements[i].value;
2515
2516                 if (!disabled && name){
2517                     switch (el.type)
2518                             {
2519                         case 'select-one':
2520                         case 'select-multiple':
2521                             for (var j = 0; j < el.options.length; j++) {
2522                                 if (el.options[j].selected) {
2523                                     if (Roo.isIE) {
2524                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2525                                     }
2526                                     else {
2527                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2528                                     }
2529                                 }
2530                             }
2531                             break;
2532                         case 'radio':
2533                         case 'checkbox':
2534                             if (el.checked) {
2535                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2536                             }
2537                             break;
2538                         case 'file':
2539
2540                         case undefined:
2541
2542                         case 'reset':
2543
2544                         case 'button':
2545
2546                             break;
2547                         case 'submit':
2548                             if(hasSubmit == false) {
2549                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2550                                 hasSubmit = true;
2551                             }
2552                             break;
2553                         default:
2554                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2555                             break;
2556                     }
2557                 }
2558             }
2559             data = data.substr(0, data.length - 1);
2560             return data;
2561         },
2562
2563         headers:{},
2564
2565         hasHeaders:false,
2566
2567         useDefaultHeader:true,
2568
2569         defaultPostHeader:'application/x-www-form-urlencoded',
2570
2571         useDefaultXhrHeader:true,
2572
2573         defaultXhrHeader:'XMLHttpRequest',
2574
2575         hasDefaultHeaders:true,
2576
2577         defaultHeaders:{},
2578
2579         poll:{},
2580
2581         timeout:{},
2582
2583         pollInterval:50,
2584
2585         transactionId:0,
2586
2587         setProgId:function(id)
2588         {
2589             this.activeX.unshift(id);
2590         },
2591
2592         setDefaultPostHeader:function(b)
2593         {
2594             this.useDefaultHeader = b;
2595         },
2596
2597         setDefaultXhrHeader:function(b)
2598         {
2599             this.useDefaultXhrHeader = b;
2600         },
2601
2602         setPollingInterval:function(i)
2603         {
2604             if (typeof i == 'number' && isFinite(i)) {
2605                 this.pollInterval = i;
2606             }
2607         },
2608
2609         createXhrObject:function(transactionId)
2610         {
2611             var obj,http;
2612             try
2613             {
2614
2615                 http = new XMLHttpRequest();
2616
2617                 obj = { conn:http, tId:transactionId };
2618             }
2619             catch(e)
2620             {
2621                 for (var i = 0; i < this.activeX.length; ++i) {
2622                     try
2623                     {
2624
2625                         http = new ActiveXObject(this.activeX[i]);
2626
2627                         obj = { conn:http, tId:transactionId };
2628                         break;
2629                     }
2630                     catch(e) {
2631                     }
2632                 }
2633             }
2634             finally
2635             {
2636                 return obj;
2637             }
2638         },
2639
2640         getConnectionObject:function()
2641         {
2642             var o;
2643             var tId = this.transactionId;
2644
2645             try
2646             {
2647                 o = this.createXhrObject(tId);
2648                 if (o) {
2649                     this.transactionId++;
2650                 }
2651             }
2652             catch(e) {
2653             }
2654             finally
2655             {
2656                 return o;
2657             }
2658         },
2659
2660         asyncRequest:function(method, uri, callback, postData)
2661         {
2662             var o = this.getConnectionObject();
2663
2664             if (!o) {
2665                 return null;
2666             }
2667             else {
2668                 o.conn.open(method, uri, true);
2669
2670                 if (this.useDefaultXhrHeader) {
2671                     if (!this.defaultHeaders['X-Requested-With']) {
2672                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2673                     }
2674                 }
2675
2676                 if(postData && this.useDefaultHeader){
2677                     this.initHeader('Content-Type', this.defaultPostHeader);
2678                 }
2679
2680                  if (this.hasDefaultHeaders || this.hasHeaders) {
2681                     this.setHeader(o);
2682                 }
2683
2684                 this.handleReadyState(o, callback);
2685                 o.conn.send(postData || null);
2686
2687                 return o;
2688             }
2689         },
2690
2691         handleReadyState:function(o, callback)
2692         {
2693             var oConn = this;
2694
2695             if (callback && callback.timeout) {
2696                 this.timeout[o.tId] = window.setTimeout(function() {
2697                     oConn.abort(o, callback, true);
2698                 }, callback.timeout);
2699             }
2700
2701             this.poll[o.tId] = window.setInterval(
2702                     function() {
2703                         if (o.conn && o.conn.readyState == 4) {
2704                             window.clearInterval(oConn.poll[o.tId]);
2705                             delete oConn.poll[o.tId];
2706
2707                             if(callback && callback.timeout) {
2708                                 window.clearTimeout(oConn.timeout[o.tId]);
2709                                 delete oConn.timeout[o.tId];
2710                             }
2711
2712                             oConn.handleTransactionResponse(o, callback);
2713                         }
2714                     }
2715                     , this.pollInterval);
2716         },
2717
2718         handleTransactionResponse:function(o, callback, isAbort)
2719         {
2720
2721             if (!callback) {
2722                 this.releaseObject(o);
2723                 return;
2724             }
2725
2726             var httpStatus, responseObject;
2727
2728             try
2729             {
2730                 if (o.conn.status !== undefined && o.conn.status != 0) {
2731                     httpStatus = o.conn.status;
2732                 }
2733                 else {
2734                     httpStatus = 13030;
2735                 }
2736             }
2737             catch(e) {
2738
2739
2740                 httpStatus = 13030;
2741             }
2742
2743             if (httpStatus >= 200 && httpStatus < 300) {
2744                 responseObject = this.createResponseObject(o, callback.argument);
2745                 if (callback.success) {
2746                     if (!callback.scope) {
2747                         callback.success(responseObject);
2748                     }
2749                     else {
2750
2751
2752                         callback.success.apply(callback.scope, [responseObject]);
2753                     }
2754                 }
2755             }
2756             else {
2757                 switch (httpStatus) {
2758
2759                     case 12002:
2760                     case 12029:
2761                     case 12030:
2762                     case 12031:
2763                     case 12152:
2764                     case 13030:
2765                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2766                         if (callback.failure) {
2767                             if (!callback.scope) {
2768                                 callback.failure(responseObject);
2769                             }
2770                             else {
2771                                 callback.failure.apply(callback.scope, [responseObject]);
2772                             }
2773                         }
2774                         break;
2775                     default:
2776                         responseObject = this.createResponseObject(o, callback.argument);
2777                         if (callback.failure) {
2778                             if (!callback.scope) {
2779                                 callback.failure(responseObject);
2780                             }
2781                             else {
2782                                 callback.failure.apply(callback.scope, [responseObject]);
2783                             }
2784                         }
2785                 }
2786             }
2787
2788             this.releaseObject(o);
2789             responseObject = null;
2790         },
2791
2792         createResponseObject:function(o, callbackArg)
2793         {
2794             var obj = {};
2795             var headerObj = {};
2796
2797             try
2798             {
2799                 var headerStr = o.conn.getAllResponseHeaders();
2800                 var header = headerStr.split('\n');
2801                 for (var i = 0; i < header.length; i++) {
2802                     var delimitPos = header[i].indexOf(':');
2803                     if (delimitPos != -1) {
2804                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2805                     }
2806                 }
2807             }
2808             catch(e) {
2809             }
2810
2811             obj.tId = o.tId;
2812             obj.status = o.conn.status;
2813             obj.statusText = o.conn.statusText;
2814             obj.getResponseHeader = headerObj;
2815             obj.getAllResponseHeaders = headerStr;
2816             obj.responseText = o.conn.responseText;
2817             obj.responseXML = o.conn.responseXML;
2818
2819             if (typeof callbackArg !== undefined) {
2820                 obj.argument = callbackArg;
2821             }
2822
2823             return obj;
2824         },
2825
2826         createExceptionObject:function(tId, callbackArg, isAbort)
2827         {
2828             var COMM_CODE = 0;
2829             var COMM_ERROR = 'communication failure';
2830             var ABORT_CODE = -1;
2831             var ABORT_ERROR = 'transaction aborted';
2832
2833             var obj = {};
2834
2835             obj.tId = tId;
2836             if (isAbort) {
2837                 obj.status = ABORT_CODE;
2838                 obj.statusText = ABORT_ERROR;
2839             }
2840             else {
2841                 obj.status = COMM_CODE;
2842                 obj.statusText = COMM_ERROR;
2843             }
2844
2845             if (callbackArg) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         initHeader:function(label, value, isDefault)
2853         {
2854             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2855
2856             if (headerObj[label] === undefined) {
2857                 headerObj[label] = value;
2858             }
2859             else {
2860
2861
2862                 headerObj[label] = value + "," + headerObj[label];
2863             }
2864
2865             if (isDefault) {
2866                 this.hasDefaultHeaders = true;
2867             }
2868             else {
2869                 this.hasHeaders = true;
2870             }
2871         },
2872
2873
2874         setHeader:function(o)
2875         {
2876             if (this.hasDefaultHeaders) {
2877                 for (var prop in this.defaultHeaders) {
2878                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2879                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2880                     }
2881                 }
2882             }
2883
2884             if (this.hasHeaders) {
2885                 for (var prop in this.headers) {
2886                     if (this.headers.hasOwnProperty(prop)) {
2887                         o.conn.setRequestHeader(prop, this.headers[prop]);
2888                     }
2889                 }
2890                 this.headers = {};
2891                 this.hasHeaders = false;
2892             }
2893         },
2894
2895         resetDefaultHeaders:function() {
2896             delete this.defaultHeaders;
2897             this.defaultHeaders = {};
2898             this.hasDefaultHeaders = false;
2899         },
2900
2901         abort:function(o, callback, isTimeout)
2902         {
2903             if(this.isCallInProgress(o)) {
2904                 o.conn.abort();
2905                 window.clearInterval(this.poll[o.tId]);
2906                 delete this.poll[o.tId];
2907                 if (isTimeout) {
2908                     delete this.timeout[o.tId];
2909                 }
2910
2911                 this.handleTransactionResponse(o, callback, true);
2912
2913                 return true;
2914             }
2915             else {
2916                 return false;
2917             }
2918         },
2919
2920
2921         isCallInProgress:function(o)
2922         {
2923             if (o && o.conn) {
2924                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2925             }
2926             else {
2927
2928                 return false;
2929             }
2930         },
2931
2932
2933         releaseObject:function(o)
2934         {
2935
2936             o.conn = null;
2937
2938             o = null;
2939         },
2940
2941         activeX:[
2942         'MSXML2.XMLHTTP.3.0',
2943         'MSXML2.XMLHTTP',
2944         'Microsoft.XMLHTTP'
2945         ]
2946
2947
2948     };
2949 })();/*
2950  * Portions of this file are based on pieces of Yahoo User Interface Library
2951  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2952  * YUI licensed under the BSD License:
2953  * http://developer.yahoo.net/yui/license.txt
2954  * <script type="text/javascript">
2955  *
2956  */
2957
2958 Roo.lib.Region = function(t, r, b, l) {
2959     this.top = t;
2960     this[1] = t;
2961     this.right = r;
2962     this.bottom = b;
2963     this.left = l;
2964     this[0] = l;
2965 };
2966
2967
2968 Roo.lib.Region.prototype = {
2969     contains : function(region) {
2970         return ( region.left >= this.left &&
2971                  region.right <= this.right &&
2972                  region.top >= this.top &&
2973                  region.bottom <= this.bottom    );
2974
2975     },
2976
2977     getArea : function() {
2978         return ( (this.bottom - this.top) * (this.right - this.left) );
2979     },
2980
2981     intersect : function(region) {
2982         var t = Math.max(this.top, region.top);
2983         var r = Math.min(this.right, region.right);
2984         var b = Math.min(this.bottom, region.bottom);
2985         var l = Math.max(this.left, region.left);
2986
2987         if (b >= t && r >= l) {
2988             return new Roo.lib.Region(t, r, b, l);
2989         } else {
2990             return null;
2991         }
2992     },
2993     union : function(region) {
2994         var t = Math.min(this.top, region.top);
2995         var r = Math.max(this.right, region.right);
2996         var b = Math.max(this.bottom, region.bottom);
2997         var l = Math.min(this.left, region.left);
2998
2999         return new Roo.lib.Region(t, r, b, l);
3000     },
3001
3002     adjust : function(t, l, b, r) {
3003         this.top += t;
3004         this.left += l;
3005         this.right += r;
3006         this.bottom += b;
3007         return this;
3008     }
3009 };
3010
3011 Roo.lib.Region.getRegion = function(el) {
3012     var p = Roo.lib.Dom.getXY(el);
3013
3014     var t = p[1];
3015     var r = p[0] + el.offsetWidth;
3016     var b = p[1] + el.offsetHeight;
3017     var l = p[0];
3018
3019     return new Roo.lib.Region(t, r, b, l);
3020 };
3021 /*
3022  * Portions of this file are based on pieces of Yahoo User Interface Library
3023  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3024  * YUI licensed under the BSD License:
3025  * http://developer.yahoo.net/yui/license.txt
3026  * <script type="text/javascript">
3027  *
3028  */
3029 //@@dep Roo.lib.Region
3030
3031
3032 Roo.lib.Point = function(x, y) {
3033     if (x instanceof Array) {
3034         y = x[1];
3035         x = x[0];
3036     }
3037     this.x = this.right = this.left = this[0] = x;
3038     this.y = this.top = this.bottom = this[1] = y;
3039 };
3040
3041 Roo.lib.Point.prototype = new Roo.lib.Region();
3042 /*
3043  * Portions of this file are based on pieces of Yahoo User Interface Library
3044  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3045  * YUI licensed under the BSD License:
3046  * http://developer.yahoo.net/yui/license.txt
3047  * <script type="text/javascript">
3048  *
3049  */
3050  
3051 (function() {   
3052
3053     Roo.lib.Anim = {
3054         scroll : function(el, args, duration, easing, cb, scope) {
3055             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3056         },
3057
3058         motion : function(el, args, duration, easing, cb, scope) {
3059             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3060         },
3061
3062         color : function(el, args, duration, easing, cb, scope) {
3063             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3064         },
3065
3066         run : function(el, args, duration, easing, cb, scope, type) {
3067             type = type || Roo.lib.AnimBase;
3068             if (typeof easing == "string") {
3069                 easing = Roo.lib.Easing[easing];
3070             }
3071             var anim = new type(el, args, duration, easing);
3072             anim.animateX(function() {
3073                 Roo.callback(cb, scope);
3074             });
3075             return anim;
3076         }
3077     };
3078 })();/*
3079  * Portions of this file are based on pieces of Yahoo User Interface Library
3080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3081  * YUI licensed under the BSD License:
3082  * http://developer.yahoo.net/yui/license.txt
3083  * <script type="text/javascript">
3084  *
3085  */
3086
3087 (function() {    
3088     var libFlyweight;
3089     
3090     function fly(el) {
3091         if (!libFlyweight) {
3092             libFlyweight = new Roo.Element.Flyweight();
3093         }
3094         libFlyweight.dom = el;
3095         return libFlyweight;
3096     }
3097
3098     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3099     
3100    
3101     
3102     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3103         if (el) {
3104             this.init(el, attributes, duration, method);
3105         }
3106     };
3107
3108     Roo.lib.AnimBase.fly = fly;
3109     
3110     
3111     
3112     Roo.lib.AnimBase.prototype = {
3113
3114         toString: function() {
3115             var el = this.getEl();
3116             var id = el.id || el.tagName;
3117             return ("Anim " + id);
3118         },
3119
3120         patterns: {
3121             noNegatives:        /width|height|opacity|padding/i,
3122             offsetAttribute:  /^((width|height)|(top|left))$/,
3123             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3124             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3125         },
3126
3127
3128         doMethod: function(attr, start, end) {
3129             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3130         },
3131
3132
3133         setAttribute: function(attr, val, unit) {
3134             if (this.patterns.noNegatives.test(attr)) {
3135                 val = (val > 0) ? val : 0;
3136             }
3137
3138             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3139         },
3140
3141
3142         getAttribute: function(attr) {
3143             var el = this.getEl();
3144             var val = fly(el).getStyle(attr);
3145
3146             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3147                 return parseFloat(val);
3148             }
3149
3150             var a = this.patterns.offsetAttribute.exec(attr) || [];
3151             var pos = !!( a[3] );
3152             var box = !!( a[2] );
3153
3154
3155             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3156                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3157             } else {
3158                 val = 0;
3159             }
3160
3161             return val;
3162         },
3163
3164
3165         getDefaultUnit: function(attr) {
3166             if (this.patterns.defaultUnit.test(attr)) {
3167                 return 'px';
3168             }
3169
3170             return '';
3171         },
3172
3173         animateX : function(callback, scope) {
3174             var f = function() {
3175                 this.onComplete.removeListener(f);
3176                 if (typeof callback == "function") {
3177                     callback.call(scope || this, this);
3178                 }
3179             };
3180             this.onComplete.addListener(f, this);
3181             this.animate();
3182         },
3183
3184
3185         setRuntimeAttribute: function(attr) {
3186             var start;
3187             var end;
3188             var attributes = this.attributes;
3189
3190             this.runtimeAttributes[attr] = {};
3191
3192             var isset = function(prop) {
3193                 return (typeof prop !== 'undefined');
3194             };
3195
3196             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3197                 return false;
3198             }
3199
3200             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3201
3202
3203             if (isset(attributes[attr]['to'])) {
3204                 end = attributes[attr]['to'];
3205             } else if (isset(attributes[attr]['by'])) {
3206                 if (start.constructor == Array) {
3207                     end = [];
3208                     for (var i = 0, len = start.length; i < len; ++i) {
3209                         end[i] = start[i] + attributes[attr]['by'][i];
3210                     }
3211                 } else {
3212                     end = start + attributes[attr]['by'];
3213                 }
3214             }
3215
3216             this.runtimeAttributes[attr].start = start;
3217             this.runtimeAttributes[attr].end = end;
3218
3219
3220             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3221         },
3222
3223
3224         init: function(el, attributes, duration, method) {
3225
3226             var isAnimated = false;
3227
3228
3229             var startTime = null;
3230
3231
3232             var actualFrames = 0;
3233
3234
3235             el = Roo.getDom(el);
3236
3237
3238             this.attributes = attributes || {};
3239
3240
3241             this.duration = duration || 1;
3242
3243
3244             this.method = method || Roo.lib.Easing.easeNone;
3245
3246
3247             this.useSeconds = true;
3248
3249
3250             this.currentFrame = 0;
3251
3252
3253             this.totalFrames = Roo.lib.AnimMgr.fps;
3254
3255
3256             this.getEl = function() {
3257                 return el;
3258             };
3259
3260
3261             this.isAnimated = function() {
3262                 return isAnimated;
3263             };
3264
3265
3266             this.getStartTime = function() {
3267                 return startTime;
3268             };
3269
3270             this.runtimeAttributes = {};
3271
3272
3273             this.animate = function() {
3274                 if (this.isAnimated()) {
3275                     return false;
3276                 }
3277
3278                 this.currentFrame = 0;
3279
3280                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3281
3282                 Roo.lib.AnimMgr.registerElement(this);
3283             };
3284
3285
3286             this.stop = function(finish) {
3287                 if (finish) {
3288                     this.currentFrame = this.totalFrames;
3289                     this._onTween.fire();
3290                 }
3291                 Roo.lib.AnimMgr.stop(this);
3292             };
3293
3294             var onStart = function() {
3295                 this.onStart.fire();
3296
3297                 this.runtimeAttributes = {};
3298                 for (var attr in this.attributes) {
3299                     this.setRuntimeAttribute(attr);
3300                 }
3301
3302                 isAnimated = true;
3303                 actualFrames = 0;
3304                 startTime = new Date();
3305             };
3306
3307
3308             var onTween = function() {
3309                 var data = {
3310                     duration: new Date() - this.getStartTime(),
3311                     currentFrame: this.currentFrame
3312                 };
3313
3314                 data.toString = function() {
3315                     return (
3316                             'duration: ' + data.duration +
3317                             ', currentFrame: ' + data.currentFrame
3318                             );
3319                 };
3320
3321                 this.onTween.fire(data);
3322
3323                 var runtimeAttributes = this.runtimeAttributes;
3324
3325                 for (var attr in runtimeAttributes) {
3326                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3327                 }
3328
3329                 actualFrames += 1;
3330             };
3331
3332             var onComplete = function() {
3333                 var actual_duration = (new Date() - startTime) / 1000 ;
3334
3335                 var data = {
3336                     duration: actual_duration,
3337                     frames: actualFrames,
3338                     fps: actualFrames / actual_duration
3339                 };
3340
3341                 data.toString = function() {
3342                     return (
3343                             'duration: ' + data.duration +
3344                             ', frames: ' + data.frames +
3345                             ', fps: ' + data.fps
3346                             );
3347                 };
3348
3349                 isAnimated = false;
3350                 actualFrames = 0;
3351                 this.onComplete.fire(data);
3352             };
3353
3354
3355             this._onStart = new Roo.util.Event(this);
3356             this.onStart = new Roo.util.Event(this);
3357             this.onTween = new Roo.util.Event(this);
3358             this._onTween = new Roo.util.Event(this);
3359             this.onComplete = new Roo.util.Event(this);
3360             this._onComplete = new Roo.util.Event(this);
3361             this._onStart.addListener(onStart);
3362             this._onTween.addListener(onTween);
3363             this._onComplete.addListener(onComplete);
3364         }
3365     };
3366 })();
3367 /*
3368  * Portions of this file are based on pieces of Yahoo User Interface Library
3369  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3370  * YUI licensed under the BSD License:
3371  * http://developer.yahoo.net/yui/license.txt
3372  * <script type="text/javascript">
3373  *
3374  */
3375
3376 Roo.lib.AnimMgr = new function() {
3377
3378         var thread = null;
3379
3380
3381         var queue = [];
3382
3383
3384         var tweenCount = 0;
3385
3386
3387         this.fps = 1000;
3388
3389
3390         this.delay = 1;
3391
3392
3393         this.registerElement = function(tween) {
3394             queue[queue.length] = tween;
3395             tweenCount += 1;
3396             tween._onStart.fire();
3397             this.start();
3398         };
3399
3400
3401         this.unRegister = function(tween, index) {
3402             tween._onComplete.fire();
3403             index = index || getIndex(tween);
3404             if (index != -1) {
3405                 queue.splice(index, 1);
3406             }
3407
3408             tweenCount -= 1;
3409             if (tweenCount <= 0) {
3410                 this.stop();
3411             }
3412         };
3413
3414
3415         this.start = function() {
3416             if (thread === null) {
3417                 thread = setInterval(this.run, this.delay);
3418             }
3419         };
3420
3421
3422         this.stop = function(tween) {
3423             if (!tween) {
3424                 clearInterval(thread);
3425
3426                 for (var i = 0, len = queue.length; i < len; ++i) {
3427                     if (queue[0].isAnimated()) {
3428                         this.unRegister(queue[0], 0);
3429                     }
3430                 }
3431
3432                 queue = [];
3433                 thread = null;
3434                 tweenCount = 0;
3435             }
3436             else {
3437                 this.unRegister(tween);
3438             }
3439         };
3440
3441
3442         this.run = function() {
3443             for (var i = 0, len = queue.length; i < len; ++i) {
3444                 var tween = queue[i];
3445                 if (!tween || !tween.isAnimated()) {
3446                     continue;
3447                 }
3448
3449                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3450                 {
3451                     tween.currentFrame += 1;
3452
3453                     if (tween.useSeconds) {
3454                         correctFrame(tween);
3455                     }
3456                     tween._onTween.fire();
3457                 }
3458                 else {
3459                     Roo.lib.AnimMgr.stop(tween, i);
3460                 }
3461             }
3462         };
3463
3464         var getIndex = function(anim) {
3465             for (var i = 0, len = queue.length; i < len; ++i) {
3466                 if (queue[i] == anim) {
3467                     return i;
3468                 }
3469             }
3470             return -1;
3471         };
3472
3473
3474         var correctFrame = function(tween) {
3475             var frames = tween.totalFrames;
3476             var frame = tween.currentFrame;
3477             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3478             var elapsed = (new Date() - tween.getStartTime());
3479             var tweak = 0;
3480
3481             if (elapsed < tween.duration * 1000) {
3482                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3483             } else {
3484                 tweak = frames - (frame + 1);
3485             }
3486             if (tweak > 0 && isFinite(tweak)) {
3487                 if (tween.currentFrame + tweak >= frames) {
3488                     tweak = frames - (frame + 1);
3489                 }
3490
3491                 tween.currentFrame += tweak;
3492             }
3493         };
3494     };/*
3495  * Portions of this file are based on pieces of Yahoo User Interface Library
3496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3497  * YUI licensed under the BSD License:
3498  * http://developer.yahoo.net/yui/license.txt
3499  * <script type="text/javascript">
3500  *
3501  */
3502 Roo.lib.Bezier = new function() {
3503
3504         this.getPosition = function(points, t) {
3505             var n = points.length;
3506             var tmp = [];
3507
3508             for (var i = 0; i < n; ++i) {
3509                 tmp[i] = [points[i][0], points[i][1]];
3510             }
3511
3512             for (var j = 1; j < n; ++j) {
3513                 for (i = 0; i < n - j; ++i) {
3514                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3515                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3516                 }
3517             }
3518
3519             return [ tmp[0][0], tmp[0][1] ];
3520
3521         };
3522     };/*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 (function() {
3531
3532     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3533         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3534     };
3535
3536     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3537
3538     var fly = Roo.lib.AnimBase.fly;
3539     var Y = Roo.lib;
3540     var superclass = Y.ColorAnim.superclass;
3541     var proto = Y.ColorAnim.prototype;
3542
3543     proto.toString = function() {
3544         var el = this.getEl();
3545         var id = el.id || el.tagName;
3546         return ("ColorAnim " + id);
3547     };
3548
3549     proto.patterns.color = /color$/i;
3550     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3551     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3552     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3553     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3554
3555
3556     proto.parseColor = function(s) {
3557         if (s.length == 3) {
3558             return s;
3559         }
3560
3561         var c = this.patterns.hex.exec(s);
3562         if (c && c.length == 4) {
3563             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3564         }
3565
3566         c = this.patterns.rgb.exec(s);
3567         if (c && c.length == 4) {
3568             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3569         }
3570
3571         c = this.patterns.hex3.exec(s);
3572         if (c && c.length == 4) {
3573             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3574         }
3575
3576         return null;
3577     };
3578     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3579     proto.getAttribute = function(attr) {
3580         var el = this.getEl();
3581         if (this.patterns.color.test(attr)) {
3582             var val = fly(el).getStyle(attr);
3583
3584             if (this.patterns.transparent.test(val)) {
3585                 var parent = el.parentNode;
3586                 val = fly(parent).getStyle(attr);
3587
3588                 while (parent && this.patterns.transparent.test(val)) {
3589                     parent = parent.parentNode;
3590                     val = fly(parent).getStyle(attr);
3591                     if (parent.tagName.toUpperCase() == 'HTML') {
3592                         val = '#fff';
3593                     }
3594                 }
3595             }
3596         } else {
3597             val = superclass.getAttribute.call(this, attr);
3598         }
3599
3600         return val;
3601     };
3602     proto.getAttribute = function(attr) {
3603         var el = this.getEl();
3604         if (this.patterns.color.test(attr)) {
3605             var val = fly(el).getStyle(attr);
3606
3607             if (this.patterns.transparent.test(val)) {
3608                 var parent = el.parentNode;
3609                 val = fly(parent).getStyle(attr);
3610
3611                 while (parent && this.patterns.transparent.test(val)) {
3612                     parent = parent.parentNode;
3613                     val = fly(parent).getStyle(attr);
3614                     if (parent.tagName.toUpperCase() == 'HTML') {
3615                         val = '#fff';
3616                     }
3617                 }
3618             }
3619         } else {
3620             val = superclass.getAttribute.call(this, attr);
3621         }
3622
3623         return val;
3624     };
3625
3626     proto.doMethod = function(attr, start, end) {
3627         var val;
3628
3629         if (this.patterns.color.test(attr)) {
3630             val = [];
3631             for (var i = 0, len = start.length; i < len; ++i) {
3632                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3633             }
3634
3635             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3636         }
3637         else {
3638             val = superclass.doMethod.call(this, attr, start, end);
3639         }
3640
3641         return val;
3642     };
3643
3644     proto.setRuntimeAttribute = function(attr) {
3645         superclass.setRuntimeAttribute.call(this, attr);
3646
3647         if (this.patterns.color.test(attr)) {
3648             var attributes = this.attributes;
3649             var start = this.parseColor(this.runtimeAttributes[attr].start);
3650             var end = this.parseColor(this.runtimeAttributes[attr].end);
3651
3652             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3653                 end = this.parseColor(attributes[attr].by);
3654
3655                 for (var i = 0, len = start.length; i < len; ++i) {
3656                     end[i] = start[i] + end[i];
3657                 }
3658             }
3659
3660             this.runtimeAttributes[attr].start = start;
3661             this.runtimeAttributes[attr].end = end;
3662         }
3663     };
3664 })();
3665
3666 /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Easing = {
3675
3676
3677     easeNone: function (t, b, c, d) {
3678         return c * t / d + b;
3679     },
3680
3681
3682     easeIn: function (t, b, c, d) {
3683         return c * (t /= d) * t + b;
3684     },
3685
3686
3687     easeOut: function (t, b, c, d) {
3688         return -c * (t /= d) * (t - 2) + b;
3689     },
3690
3691
3692     easeBoth: function (t, b, c, d) {
3693         if ((t /= d / 2) < 1) {
3694             return c / 2 * t * t + b;
3695         }
3696
3697         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3698     },
3699
3700
3701     easeInStrong: function (t, b, c, d) {
3702         return c * (t /= d) * t * t * t + b;
3703     },
3704
3705
3706     easeOutStrong: function (t, b, c, d) {
3707         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3708     },
3709
3710
3711     easeBothStrong: function (t, b, c, d) {
3712         if ((t /= d / 2) < 1) {
3713             return c / 2 * t * t * t * t + b;
3714         }
3715
3716         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3717     },
3718
3719
3720
3721     elasticIn: function (t, b, c, d, a, p) {
3722         if (t == 0) {
3723             return b;
3724         }
3725         if ((t /= d) == 1) {
3726             return b + c;
3727         }
3728         if (!p) {
3729             p = d * .3;
3730         }
3731
3732         if (!a || a < Math.abs(c)) {
3733             a = c;
3734             var s = p / 4;
3735         }
3736         else {
3737             var s = p / (2 * Math.PI) * Math.asin(c / a);
3738         }
3739
3740         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3741     },
3742
3743
3744     elasticOut: function (t, b, c, d, a, p) {
3745         if (t == 0) {
3746             return b;
3747         }
3748         if ((t /= d) == 1) {
3749             return b + c;
3750         }
3751         if (!p) {
3752             p = d * .3;
3753         }
3754
3755         if (!a || a < Math.abs(c)) {
3756             a = c;
3757             var s = p / 4;
3758         }
3759         else {
3760             var s = p / (2 * Math.PI) * Math.asin(c / a);
3761         }
3762
3763         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3764     },
3765
3766
3767     elasticBoth: function (t, b, c, d, a, p) {
3768         if (t == 0) {
3769             return b;
3770         }
3771
3772         if ((t /= d / 2) == 2) {
3773             return b + c;
3774         }
3775
3776         if (!p) {
3777             p = d * (.3 * 1.5);
3778         }
3779
3780         if (!a || a < Math.abs(c)) {
3781             a = c;
3782             var s = p / 4;
3783         }
3784         else {
3785             var s = p / (2 * Math.PI) * Math.asin(c / a);
3786         }
3787
3788         if (t < 1) {
3789             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3790                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3791         }
3792         return a * Math.pow(2, -10 * (t -= 1)) *
3793                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3794     },
3795
3796
3797
3798     backIn: function (t, b, c, d, s) {
3799         if (typeof s == 'undefined') {
3800             s = 1.70158;
3801         }
3802         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3803     },
3804
3805
3806     backOut: function (t, b, c, d, s) {
3807         if (typeof s == 'undefined') {
3808             s = 1.70158;
3809         }
3810         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3811     },
3812
3813
3814     backBoth: function (t, b, c, d, s) {
3815         if (typeof s == 'undefined') {
3816             s = 1.70158;
3817         }
3818
3819         if ((t /= d / 2 ) < 1) {
3820             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3821         }
3822         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3823     },
3824
3825
3826     bounceIn: function (t, b, c, d) {
3827         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3828     },
3829
3830
3831     bounceOut: function (t, b, c, d) {
3832         if ((t /= d) < (1 / 2.75)) {
3833             return c * (7.5625 * t * t) + b;
3834         } else if (t < (2 / 2.75)) {
3835             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3836         } else if (t < (2.5 / 2.75)) {
3837             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3838         }
3839         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3840     },
3841
3842
3843     bounceBoth: function (t, b, c, d) {
3844         if (t < d / 2) {
3845             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3846         }
3847         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3848     }
3849 };/*
3850  * Portions of this file are based on pieces of Yahoo User Interface Library
3851  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3852  * YUI licensed under the BSD License:
3853  * http://developer.yahoo.net/yui/license.txt
3854  * <script type="text/javascript">
3855  *
3856  */
3857     (function() {
3858         Roo.lib.Motion = function(el, attributes, duration, method) {
3859             if (el) {
3860                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3861             }
3862         };
3863
3864         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3865
3866
3867         var Y = Roo.lib;
3868         var superclass = Y.Motion.superclass;
3869         var proto = Y.Motion.prototype;
3870
3871         proto.toString = function() {
3872             var el = this.getEl();
3873             var id = el.id || el.tagName;
3874             return ("Motion " + id);
3875         };
3876
3877         proto.patterns.points = /^points$/i;
3878
3879         proto.setAttribute = function(attr, val, unit) {
3880             if (this.patterns.points.test(attr)) {
3881                 unit = unit || 'px';
3882                 superclass.setAttribute.call(this, 'left', val[0], unit);
3883                 superclass.setAttribute.call(this, 'top', val[1], unit);
3884             } else {
3885                 superclass.setAttribute.call(this, attr, val, unit);
3886             }
3887         };
3888
3889         proto.getAttribute = function(attr) {
3890             if (this.patterns.points.test(attr)) {
3891                 var val = [
3892                         superclass.getAttribute.call(this, 'left'),
3893                         superclass.getAttribute.call(this, 'top')
3894                         ];
3895             } else {
3896                 val = superclass.getAttribute.call(this, attr);
3897             }
3898
3899             return val;
3900         };
3901
3902         proto.doMethod = function(attr, start, end) {
3903             var val = null;
3904
3905             if (this.patterns.points.test(attr)) {
3906                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3907                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3908             } else {
3909                 val = superclass.doMethod.call(this, attr, start, end);
3910             }
3911             return val;
3912         };
3913
3914         proto.setRuntimeAttribute = function(attr) {
3915             if (this.patterns.points.test(attr)) {
3916                 var el = this.getEl();
3917                 var attributes = this.attributes;
3918                 var start;
3919                 var control = attributes['points']['control'] || [];
3920                 var end;
3921                 var i, len;
3922
3923                 if (control.length > 0 && !(control[0] instanceof Array)) {
3924                     control = [control];
3925                 } else {
3926                     var tmp = [];
3927                     for (i = 0,len = control.length; i < len; ++i) {
3928                         tmp[i] = control[i];
3929                     }
3930                     control = tmp;
3931                 }
3932
3933                 Roo.fly(el).position();
3934
3935                 if (isset(attributes['points']['from'])) {
3936                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3937                 }
3938                 else {
3939                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3940                 }
3941
3942                 start = this.getAttribute('points');
3943
3944
3945                 if (isset(attributes['points']['to'])) {
3946                     end = translateValues.call(this, attributes['points']['to'], start);
3947
3948                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3949                     for (i = 0,len = control.length; i < len; ++i) {
3950                         control[i] = translateValues.call(this, control[i], start);
3951                     }
3952
3953
3954                 } else if (isset(attributes['points']['by'])) {
3955                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3956
3957                     for (i = 0,len = control.length; i < len; ++i) {
3958                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3959                     }
3960                 }
3961
3962                 this.runtimeAttributes[attr] = [start];
3963
3964                 if (control.length > 0) {
3965                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3966                 }
3967
3968                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3969             }
3970             else {
3971                 superclass.setRuntimeAttribute.call(this, attr);
3972             }
3973         };
3974
3975         var translateValues = function(val, start) {
3976             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3978
3979             return val;
3980         };
3981
3982         var isset = function(prop) {
3983             return (typeof prop !== 'undefined');
3984         };
3985     })();
3986 /*
3987  * Portions of this file are based on pieces of Yahoo User Interface Library
3988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3989  * YUI licensed under the BSD License:
3990  * http://developer.yahoo.net/yui/license.txt
3991  * <script type="text/javascript">
3992  *
3993  */
3994     (function() {
3995         Roo.lib.Scroll = function(el, attributes, duration, method) {
3996             if (el) {
3997                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3998             }
3999         };
4000
4001         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4002
4003
4004         var Y = Roo.lib;
4005         var superclass = Y.Scroll.superclass;
4006         var proto = Y.Scroll.prototype;
4007
4008         proto.toString = function() {
4009             var el = this.getEl();
4010             var id = el.id || el.tagName;
4011             return ("Scroll " + id);
4012         };
4013
4014         proto.doMethod = function(attr, start, end) {
4015             var val = null;
4016
4017             if (attr == 'scroll') {
4018                 val = [
4019                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4020                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4021                         ];
4022
4023             } else {
4024                 val = superclass.doMethod.call(this, attr, start, end);
4025             }
4026             return val;
4027         };
4028
4029         proto.getAttribute = function(attr) {
4030             var val = null;
4031             var el = this.getEl();
4032
4033             if (attr == 'scroll') {
4034                 val = [ el.scrollLeft, el.scrollTop ];
4035             } else {
4036                 val = superclass.getAttribute.call(this, attr);
4037             }
4038
4039             return val;
4040         };
4041
4042         proto.setAttribute = function(attr, val, unit) {
4043             var el = this.getEl();
4044
4045             if (attr == 'scroll') {
4046                 el.scrollLeft = val[0];
4047                 el.scrollTop = val[1];
4048             } else {
4049                 superclass.setAttribute.call(this, attr, val, unit);
4050             }
4051         };
4052     })();
4053 /*
4054  * Based on:
4055  * Ext JS Library 1.1.1
4056  * Copyright(c) 2006-2007, Ext JS, LLC.
4057  *
4058  * Originally Released Under LGPL - original licence link has changed is not relivant.
4059  *
4060  * Fork - LGPL
4061  * <script type="text/javascript">
4062  */
4063
4064
4065 // nasty IE9 hack - what a pile of crap that is..
4066
4067  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4068     Range.prototype.createContextualFragment = function (html) {
4069         var doc = window.document;
4070         var container = doc.createElement("div");
4071         container.innerHTML = html;
4072         var frag = doc.createDocumentFragment(), n;
4073         while ((n = container.firstChild)) {
4074             frag.appendChild(n);
4075         }
4076         return frag;
4077     };
4078 }
4079
4080 /**
4081  * @class Roo.DomHelper
4082  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4083  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4084  * @singleton
4085  */
4086 Roo.DomHelper = function(){
4087     var tempTableEl = null;
4088     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4089     var tableRe = /^table|tbody|tr|td$/i;
4090     var xmlns = {};
4091     // build as innerHTML where available
4092     /** @ignore */
4093     var createHtml = function(o){
4094         if(typeof o == 'string'){
4095             return o;
4096         }
4097         var b = "";
4098         if(!o.tag){
4099             o.tag = "div";
4100         }
4101         b += "<" + o.tag;
4102         for(var attr in o){
4103             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4104             if(attr == "style"){
4105                 var s = o["style"];
4106                 if(typeof s == "function"){
4107                     s = s.call();
4108                 }
4109                 if(typeof s == "string"){
4110                     b += ' style="' + s + '"';
4111                 }else if(typeof s == "object"){
4112                     b += ' style="';
4113                     for(var key in s){
4114                         if(typeof s[key] != "function"){
4115                             b += key + ":" + s[key] + ";";
4116                         }
4117                     }
4118                     b += '"';
4119                 }
4120             }else{
4121                 if(attr == "cls"){
4122                     b += ' class="' + o["cls"] + '"';
4123                 }else if(attr == "htmlFor"){
4124                     b += ' for="' + o["htmlFor"] + '"';
4125                 }else{
4126                     b += " " + attr + '="' + o[attr] + '"';
4127                 }
4128             }
4129         }
4130         if(emptyTags.test(o.tag)){
4131             b += "/>";
4132         }else{
4133             b += ">";
4134             var cn = o.children || o.cn;
4135             if(cn){
4136                 //http://bugs.kde.org/show_bug.cgi?id=71506
4137                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4138                     for(var i = 0, len = cn.length; i < len; i++) {
4139                         b += createHtml(cn[i], b);
4140                     }
4141                 }else{
4142                     b += createHtml(cn, b);
4143                 }
4144             }
4145             if(o.html){
4146                 b += o.html;
4147             }
4148             b += "</" + o.tag + ">";
4149         }
4150         return b;
4151     };
4152
4153     // build as dom
4154     /** @ignore */
4155     var createDom = function(o, parentNode){
4156          
4157         // defininition craeted..
4158         var ns = false;
4159         if (o.ns && o.ns != 'html') {
4160                
4161             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4162                 xmlns[o.ns] = o.xmlns;
4163                 ns = o.xmlns;
4164             }
4165             if (typeof(xmlns[o.ns]) == 'undefined') {
4166                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4167             }
4168             ns = xmlns[o.ns];
4169         }
4170         
4171         
4172         if (typeof(o) == 'string') {
4173             return parentNode.appendChild(document.createTextNode(o));
4174         }
4175         o.tag = o.tag || div;
4176         if (o.ns && Roo.isIE) {
4177             ns = false;
4178             o.tag = o.ns + ':' + o.tag;
4179             
4180         }
4181         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4182         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4183         for(var attr in o){
4184             
4185             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4186                     attr == "style" || typeof o[attr] == "function") continue;
4187                     
4188             if(attr=="cls" && Roo.isIE){
4189                 el.className = o["cls"];
4190             }else{
4191                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4192                 else el[attr] = o[attr];
4193             }
4194         }
4195         Roo.DomHelper.applyStyles(el, o.style);
4196         var cn = o.children || o.cn;
4197         if(cn){
4198             //http://bugs.kde.org/show_bug.cgi?id=71506
4199              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4200                 for(var i = 0, len = cn.length; i < len; i++) {
4201                     createDom(cn[i], el);
4202                 }
4203             }else{
4204                 createDom(cn, el);
4205             }
4206         }
4207         if(o.html){
4208             el.innerHTML = o.html;
4209         }
4210         if(parentNode){
4211            parentNode.appendChild(el);
4212         }
4213         return el;
4214     };
4215
4216     var ieTable = function(depth, s, h, e){
4217         tempTableEl.innerHTML = [s, h, e].join('');
4218         var i = -1, el = tempTableEl;
4219         while(++i < depth){
4220             el = el.firstChild;
4221         }
4222         return el;
4223     };
4224
4225     // kill repeat to save bytes
4226     var ts = '<table>',
4227         te = '</table>',
4228         tbs = ts+'<tbody>',
4229         tbe = '</tbody>'+te,
4230         trs = tbs + '<tr>',
4231         tre = '</tr>'+tbe;
4232
4233     /**
4234      * @ignore
4235      * Nasty code for IE's broken table implementation
4236      */
4237     var insertIntoTable = function(tag, where, el, html){
4238         if(!tempTableEl){
4239             tempTableEl = document.createElement('div');
4240         }
4241         var node;
4242         var before = null;
4243         if(tag == 'td'){
4244             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4245                 return;
4246             }
4247             if(where == 'beforebegin'){
4248                 before = el;
4249                 el = el.parentNode;
4250             } else{
4251                 before = el.nextSibling;
4252                 el = el.parentNode;
4253             }
4254             node = ieTable(4, trs, html, tre);
4255         }
4256         else if(tag == 'tr'){
4257             if(where == 'beforebegin'){
4258                 before = el;
4259                 el = el.parentNode;
4260                 node = ieTable(3, tbs, html, tbe);
4261             } else if(where == 'afterend'){
4262                 before = el.nextSibling;
4263                 el = el.parentNode;
4264                 node = ieTable(3, tbs, html, tbe);
4265             } else{ // INTO a TR
4266                 if(where == 'afterbegin'){
4267                     before = el.firstChild;
4268                 }
4269                 node = ieTable(4, trs, html, tre);
4270             }
4271         } else if(tag == 'tbody'){
4272             if(where == 'beforebegin'){
4273                 before = el;
4274                 el = el.parentNode;
4275                 node = ieTable(2, ts, html, te);
4276             } else if(where == 'afterend'){
4277                 before = el.nextSibling;
4278                 el = el.parentNode;
4279                 node = ieTable(2, ts, html, te);
4280             } else{
4281                 if(where == 'afterbegin'){
4282                     before = el.firstChild;
4283                 }
4284                 node = ieTable(3, tbs, html, tbe);
4285             }
4286         } else{ // TABLE
4287             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4288                 return;
4289             }
4290             if(where == 'afterbegin'){
4291                 before = el.firstChild;
4292             }
4293             node = ieTable(2, ts, html, te);
4294         }
4295         el.insertBefore(node, before);
4296         return node;
4297     };
4298
4299     return {
4300     /** True to force the use of DOM instead of html fragments @type Boolean */
4301     useDom : false,
4302
4303     /**
4304      * Returns the markup for the passed Element(s) config
4305      * @param {Object} o The Dom object spec (and children)
4306      * @return {String}
4307      */
4308     markup : function(o){
4309         return createHtml(o);
4310     },
4311
4312     /**
4313      * Applies a style specification to an element
4314      * @param {String/HTMLElement} el The element to apply styles to
4315      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4316      * a function which returns such a specification.
4317      */
4318     applyStyles : function(el, styles){
4319         if(styles){
4320            el = Roo.fly(el);
4321            if(typeof styles == "string"){
4322                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4323                var matches;
4324                while ((matches = re.exec(styles)) != null){
4325                    el.setStyle(matches[1], matches[2]);
4326                }
4327            }else if (typeof styles == "object"){
4328                for (var style in styles){
4329                   el.setStyle(style, styles[style]);
4330                }
4331            }else if (typeof styles == "function"){
4332                 Roo.DomHelper.applyStyles(el, styles.call());
4333            }
4334         }
4335     },
4336
4337     /**
4338      * Inserts an HTML fragment into the Dom
4339      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4340      * @param {HTMLElement} el The context element
4341      * @param {String} html The HTML fragmenet
4342      * @return {HTMLElement} The new node
4343      */
4344     insertHtml : function(where, el, html){
4345         where = where.toLowerCase();
4346         if(el.insertAdjacentHTML){
4347             if(tableRe.test(el.tagName)){
4348                 var rs;
4349                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4350                     return rs;
4351                 }
4352             }
4353             switch(where){
4354                 case "beforebegin":
4355                     el.insertAdjacentHTML('BeforeBegin', html);
4356                     return el.previousSibling;
4357                 case "afterbegin":
4358                     el.insertAdjacentHTML('AfterBegin', html);
4359                     return el.firstChild;
4360                 case "beforeend":
4361                     el.insertAdjacentHTML('BeforeEnd', html);
4362                     return el.lastChild;
4363                 case "afterend":
4364                     el.insertAdjacentHTML('AfterEnd', html);
4365                     return el.nextSibling;
4366             }
4367             throw 'Illegal insertion point -> "' + where + '"';
4368         }
4369         var range = el.ownerDocument.createRange();
4370         var frag;
4371         switch(where){
4372              case "beforebegin":
4373                 range.setStartBefore(el);
4374                 frag = range.createContextualFragment(html);
4375                 el.parentNode.insertBefore(frag, el);
4376                 return el.previousSibling;
4377              case "afterbegin":
4378                 if(el.firstChild){
4379                     range.setStartBefore(el.firstChild);
4380                     frag = range.createContextualFragment(html);
4381                     el.insertBefore(frag, el.firstChild);
4382                     return el.firstChild;
4383                 }else{
4384                     el.innerHTML = html;
4385                     return el.firstChild;
4386                 }
4387             case "beforeend":
4388                 if(el.lastChild){
4389                     range.setStartAfter(el.lastChild);
4390                     frag = range.createContextualFragment(html);
4391                     el.appendChild(frag);
4392                     return el.lastChild;
4393                 }else{
4394                     el.innerHTML = html;
4395                     return el.lastChild;
4396                 }
4397             case "afterend":
4398                 range.setStartAfter(el);
4399                 frag = range.createContextualFragment(html);
4400                 el.parentNode.insertBefore(frag, el.nextSibling);
4401                 return el.nextSibling;
4402             }
4403             throw 'Illegal insertion point -> "' + where + '"';
4404     },
4405
4406     /**
4407      * Creates new Dom element(s) and inserts them before el
4408      * @param {String/HTMLElement/Element} el The context element
4409      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4410      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4411      * @return {HTMLElement/Roo.Element} The new node
4412      */
4413     insertBefore : function(el, o, returnElement){
4414         return this.doInsert(el, o, returnElement, "beforeBegin");
4415     },
4416
4417     /**
4418      * Creates new Dom element(s) and inserts them after el
4419      * @param {String/HTMLElement/Element} el The context element
4420      * @param {Object} o The Dom object spec (and children)
4421      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4422      * @return {HTMLElement/Roo.Element} The new node
4423      */
4424     insertAfter : function(el, o, returnElement){
4425         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4426     },
4427
4428     /**
4429      * Creates new Dom element(s) and inserts them as the first child of el
4430      * @param {String/HTMLElement/Element} el The context element
4431      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4432      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4433      * @return {HTMLElement/Roo.Element} The new node
4434      */
4435     insertFirst : function(el, o, returnElement){
4436         return this.doInsert(el, o, returnElement, "afterBegin");
4437     },
4438
4439     // private
4440     doInsert : function(el, o, returnElement, pos, sibling){
4441         el = Roo.getDom(el);
4442         var newNode;
4443         if(this.useDom || o.ns){
4444             newNode = createDom(o, null);
4445             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4446         }else{
4447             var html = createHtml(o);
4448             newNode = this.insertHtml(pos, el, html);
4449         }
4450         return returnElement ? Roo.get(newNode, true) : newNode;
4451     },
4452
4453     /**
4454      * Creates new Dom element(s) and appends them to el
4455      * @param {String/HTMLElement/Element} el The context element
4456      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4457      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4458      * @return {HTMLElement/Roo.Element} The new node
4459      */
4460     append : function(el, o, returnElement){
4461         el = Roo.getDom(el);
4462         var newNode;
4463         if(this.useDom || o.ns){
4464             newNode = createDom(o, null);
4465             el.appendChild(newNode);
4466         }else{
4467             var html = createHtml(o);
4468             newNode = this.insertHtml("beforeEnd", el, html);
4469         }
4470         return returnElement ? Roo.get(newNode, true) : newNode;
4471     },
4472
4473     /**
4474      * Creates new Dom element(s) and overwrites the contents of el with them
4475      * @param {String/HTMLElement/Element} el The context element
4476      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4477      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4478      * @return {HTMLElement/Roo.Element} The new node
4479      */
4480     overwrite : function(el, o, returnElement){
4481         el = Roo.getDom(el);
4482         if (o.ns) {
4483           
4484             while (el.childNodes.length) {
4485                 el.removeChild(el.firstChild);
4486             }
4487             createDom(o, el);
4488         } else {
4489             el.innerHTML = createHtml(o);   
4490         }
4491         
4492         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4493     },
4494
4495     /**
4496      * Creates a new Roo.DomHelper.Template from the Dom object spec
4497      * @param {Object} o The Dom object spec (and children)
4498      * @return {Roo.DomHelper.Template} The new template
4499      */
4500     createTemplate : function(o){
4501         var html = createHtml(o);
4502         return new Roo.Template(html);
4503     }
4504     };
4505 }();
4506 /*
4507  * Based on:
4508  * Ext JS Library 1.1.1
4509  * Copyright(c) 2006-2007, Ext JS, LLC.
4510  *
4511  * Originally Released Under LGPL - original licence link has changed is not relivant.
4512  *
4513  * Fork - LGPL
4514  * <script type="text/javascript">
4515  */
4516  
4517 /**
4518 * @class Roo.Template
4519 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4520 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4521 * Usage:
4522 <pre><code>
4523 var t = new Roo.Template({
4524     html :  '&lt;div name="{id}"&gt;' + 
4525         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4526         '&lt;/div&gt;',
4527     myformat: function (value, allValues) {
4528         return 'XX' + value;
4529     }
4530 });
4531 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4532 </code></pre>
4533 * For more information see this blog post with examples:
4534 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4535      - Create Elements using DOM, HTML fragments and Templates</a>. 
4536 * @constructor
4537 * @param {Object} cfg - Configuration object.
4538 */
4539 Roo.Template = function(cfg){
4540     // BC!
4541     if(cfg instanceof Array){
4542         cfg = cfg.join("");
4543     }else if(arguments.length > 1){
4544         cfg = Array.prototype.join.call(arguments, "");
4545     }
4546     
4547     
4548     if (typeof(cfg) == 'object') {
4549         Roo.apply(this,cfg)
4550     } else {
4551         // bc
4552         this.html = cfg;
4553     }
4554     if (this.url) {
4555         this.load();
4556     }
4557     
4558 };
4559 Roo.Template.prototype = {
4560     
4561     /**
4562      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4563      *                    it should be fixed so that template is observable...
4564      */
4565     url : false,
4566     /**
4567      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4568      */
4569     html : '',
4570     /**
4571      * Returns an HTML fragment of this template with the specified values applied.
4572      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4573      * @return {String} The HTML fragment
4574      */
4575     applyTemplate : function(values){
4576         try {
4577            
4578             if(this.compiled){
4579                 return this.compiled(values);
4580             }
4581             var useF = this.disableFormats !== true;
4582             var fm = Roo.util.Format, tpl = this;
4583             var fn = function(m, name, format, args){
4584                 if(format && useF){
4585                     if(format.substr(0, 5) == "this."){
4586                         return tpl.call(format.substr(5), values[name], values);
4587                     }else{
4588                         if(args){
4589                             // quoted values are required for strings in compiled templates, 
4590                             // but for non compiled we need to strip them
4591                             // quoted reversed for jsmin
4592                             var re = /^\s*['"](.*)["']\s*$/;
4593                             args = args.split(',');
4594                             for(var i = 0, len = args.length; i < len; i++){
4595                                 args[i] = args[i].replace(re, "$1");
4596                             }
4597                             args = [values[name]].concat(args);
4598                         }else{
4599                             args = [values[name]];
4600                         }
4601                         return fm[format].apply(fm, args);
4602                     }
4603                 }else{
4604                     return values[name] !== undefined ? values[name] : "";
4605                 }
4606             };
4607             return this.html.replace(this.re, fn);
4608         } catch (e) {
4609             Roo.log(e);
4610             throw e;
4611         }
4612          
4613     },
4614     
4615     loading : false,
4616       
4617     load : function ()
4618     {
4619          
4620         if (this.loading) {
4621             return;
4622         }
4623         var _t = this;
4624         
4625         this.loading = true;
4626         this.compiled = false;
4627         
4628         var cx = new Roo.data.Connection();
4629         cx.request({
4630             url : this.url,
4631             method : 'GET',
4632             success : function (response) {
4633                 _t.loading = false;
4634                 _t.html = response.responseText;
4635                 _t.url = false;
4636                 _t.compile();
4637              },
4638             failure : function(response) {
4639                 Roo.log("Template failed to load from " + url);
4640                 _t.loading = false;
4641             }
4642         });
4643     },
4644
4645     /**
4646      * Sets the HTML used as the template and optionally compiles it.
4647      * @param {String} html
4648      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4649      * @return {Roo.Template} this
4650      */
4651     set : function(html, compile){
4652         this.html = html;
4653         this.compiled = null;
4654         if(compile){
4655             this.compile();
4656         }
4657         return this;
4658     },
4659     
4660     /**
4661      * True to disable format functions (defaults to false)
4662      * @type Boolean
4663      */
4664     disableFormats : false,
4665     
4666     /**
4667     * The regular expression used to match template variables 
4668     * @type RegExp
4669     * @property 
4670     */
4671     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4672     
4673     /**
4674      * Compiles the template into an internal function, eliminating the RegEx overhead.
4675      * @return {Roo.Template} this
4676      */
4677     compile : function(){
4678         var fm = Roo.util.Format;
4679         var useF = this.disableFormats !== true;
4680         var sep = Roo.isGecko ? "+" : ",";
4681         var fn = function(m, name, format, args){
4682             if(format && useF){
4683                 args = args ? ',' + args : "";
4684                 if(format.substr(0, 5) != "this."){
4685                     format = "fm." + format + '(';
4686                 }else{
4687                     format = 'this.call("'+ format.substr(5) + '", ';
4688                     args = ", values";
4689                 }
4690             }else{
4691                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4692             }
4693             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4694         };
4695         var body;
4696         // branched to use + in gecko and [].join() in others
4697         if(Roo.isGecko){
4698             body = "this.compiled = function(values){ return '" +
4699                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4700                     "';};";
4701         }else{
4702             body = ["this.compiled = function(values){ return ['"];
4703             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4704             body.push("'].join('');};");
4705             body = body.join('');
4706         }
4707         /**
4708          * eval:var:values
4709          * eval:var:fm
4710          */
4711         eval(body);
4712         return this;
4713     },
4714     
4715     // private function used to call members
4716     call : function(fnName, value, allValues){
4717         return this[fnName](value, allValues);
4718     },
4719     
4720     /**
4721      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4722      * @param {String/HTMLElement/Roo.Element} el The context element
4723      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4724      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4725      * @return {HTMLElement/Roo.Element} The new node or Element
4726      */
4727     insertFirst: function(el, values, returnElement){
4728         return this.doInsert('afterBegin', el, values, returnElement);
4729     },
4730
4731     /**
4732      * Applies the supplied values to the template and inserts the new node(s) before el.
4733      * @param {String/HTMLElement/Roo.Element} el The context element
4734      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4735      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4736      * @return {HTMLElement/Roo.Element} The new node or Element
4737      */
4738     insertBefore: function(el, values, returnElement){
4739         return this.doInsert('beforeBegin', el, values, returnElement);
4740     },
4741
4742     /**
4743      * Applies the supplied values to the template and inserts the new node(s) after el.
4744      * @param {String/HTMLElement/Roo.Element} el The context element
4745      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4746      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4747      * @return {HTMLElement/Roo.Element} The new node or Element
4748      */
4749     insertAfter : function(el, values, returnElement){
4750         return this.doInsert('afterEnd', el, values, returnElement);
4751     },
4752     
4753     /**
4754      * Applies the supplied values to the template and appends the new node(s) to el.
4755      * @param {String/HTMLElement/Roo.Element} el The context element
4756      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4757      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4758      * @return {HTMLElement/Roo.Element} The new node or Element
4759      */
4760     append : function(el, values, returnElement){
4761         return this.doInsert('beforeEnd', el, values, returnElement);
4762     },
4763
4764     doInsert : function(where, el, values, returnEl){
4765         el = Roo.getDom(el);
4766         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4767         return returnEl ? Roo.get(newNode, true) : newNode;
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     overwrite : function(el, values, returnElement){
4778         el = Roo.getDom(el);
4779         el.innerHTML = this.applyTemplate(values);
4780         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4781     }
4782 };
4783 /**
4784  * Alias for {@link #applyTemplate}
4785  * @method
4786  */
4787 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4788
4789 // backwards compat
4790 Roo.DomHelper.Template = Roo.Template;
4791
4792 /**
4793  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4794  * @param {String/HTMLElement} el A DOM element or its id
4795  * @returns {Roo.Template} The created template
4796  * @static
4797  */
4798 Roo.Template.from = function(el){
4799     el = Roo.getDom(el);
4800     return new Roo.Template(el.value || el.innerHTML);
4801 };/*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812
4813 /*
4814  * This is code is also distributed under MIT license for use
4815  * with jQuery and prototype JavaScript libraries.
4816  */
4817 /**
4818  * @class Roo.DomQuery
4819 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4820 <p>
4821 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4822
4823 <p>
4824 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4825 </p>
4826 <h4>Element Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>*</b> any element</li>
4829     <li> <b>E</b> an element with the tag E</li>
4830     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4831     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4832     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4833     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4834 </ul>
4835 <h4>Attribute Selectors:</h4>
4836 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4837 <ul class="list">
4838     <li> <b>E[foo]</b> has an attribute "foo"</li>
4839     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4840     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4841     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4842     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4843     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4844     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4845 </ul>
4846 <h4>Pseudo Classes:</h4>
4847 <ul class="list">
4848     <li> <b>E:first-child</b> E is the first child of its parent</li>
4849     <li> <b>E:last-child</b> E is the last child of its parent</li>
4850     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4851     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4852     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4853     <li> <b>E:only-child</b> E is the only child of its parent</li>
4854     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4855     <li> <b>E:first</b> the first E in the resultset</li>
4856     <li> <b>E:last</b> the last E in the resultset</li>
4857     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4858     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4859     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4860     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4861     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4862     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4863     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4864     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4865     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4866 </ul>
4867 <h4>CSS Value Selectors:</h4>
4868 <ul class="list">
4869     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4870     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4871     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4872     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4873     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4874     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4875 </ul>
4876  * @singleton
4877  */
4878 Roo.DomQuery = function(){
4879     var cache = {}, simpleCache = {}, valueCache = {};
4880     var nonSpace = /\S/;
4881     var trimRe = /^\s+|\s+$/g;
4882     var tplRe = /\{(\d+)\}/g;
4883     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4884     var tagTokenRe = /^(#)?([\w-\*]+)/;
4885     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4886
4887     function child(p, index){
4888         var i = 0;
4889         var n = p.firstChild;
4890         while(n){
4891             if(n.nodeType == 1){
4892                if(++i == index){
4893                    return n;
4894                }
4895             }
4896             n = n.nextSibling;
4897         }
4898         return null;
4899     };
4900
4901     function next(n){
4902         while((n = n.nextSibling) && n.nodeType != 1);
4903         return n;
4904     };
4905
4906     function prev(n){
4907         while((n = n.previousSibling) && n.nodeType != 1);
4908         return n;
4909     };
4910
4911     function children(d){
4912         var n = d.firstChild, ni = -1;
4913             while(n){
4914                 var nx = n.nextSibling;
4915                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4916                     d.removeChild(n);
4917                 }else{
4918                     n.nodeIndex = ++ni;
4919                 }
4920                 n = nx;
4921             }
4922             return this;
4923         };
4924
4925     function byClassName(c, a, v){
4926         if(!v){
4927             return c;
4928         }
4929         var r = [], ri = -1, cn;
4930         for(var i = 0, ci; ci = c[i]; i++){
4931             if((' '+ci.className+' ').indexOf(v) != -1){
4932                 r[++ri] = ci;
4933             }
4934         }
4935         return r;
4936     };
4937
4938     function attrValue(n, attr){
4939         if(!n.tagName && typeof n.length != "undefined"){
4940             n = n[0];
4941         }
4942         if(!n){
4943             return null;
4944         }
4945         if(attr == "for"){
4946             return n.htmlFor;
4947         }
4948         if(attr == "class" || attr == "className"){
4949             return n.className;
4950         }
4951         return n.getAttribute(attr) || n[attr];
4952
4953     };
4954
4955     function getNodes(ns, mode, tagName){
4956         var result = [], ri = -1, cs;
4957         if(!ns){
4958             return result;
4959         }
4960         tagName = tagName || "*";
4961         if(typeof ns.getElementsByTagName != "undefined"){
4962             ns = [ns];
4963         }
4964         if(!mode){
4965             for(var i = 0, ni; ni = ns[i]; i++){
4966                 cs = ni.getElementsByTagName(tagName);
4967                 for(var j = 0, ci; ci = cs[j]; j++){
4968                     result[++ri] = ci;
4969                 }
4970             }
4971         }else if(mode == "/" || mode == ">"){
4972             var utag = tagName.toUpperCase();
4973             for(var i = 0, ni, cn; ni = ns[i]; i++){
4974                 cn = ni.children || ni.childNodes;
4975                 for(var j = 0, cj; cj = cn[j]; j++){
4976                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4977                         result[++ri] = cj;
4978                     }
4979                 }
4980             }
4981         }else if(mode == "+"){
4982             var utag = tagName.toUpperCase();
4983             for(var i = 0, n; n = ns[i]; i++){
4984                 while((n = n.nextSibling) && n.nodeType != 1);
4985                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4986                     result[++ri] = n;
4987                 }
4988             }
4989         }else if(mode == "~"){
4990             for(var i = 0, n; n = ns[i]; i++){
4991                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4992                 if(n){
4993                     result[++ri] = n;
4994                 }
4995             }
4996         }
4997         return result;
4998     };
4999
5000     function concat(a, b){
5001         if(b.slice){
5002             return a.concat(b);
5003         }
5004         for(var i = 0, l = b.length; i < l; i++){
5005             a[a.length] = b[i];
5006         }
5007         return a;
5008     }
5009
5010     function byTag(cs, tagName){
5011         if(cs.tagName || cs == document){
5012             cs = [cs];
5013         }
5014         if(!tagName){
5015             return cs;
5016         }
5017         var r = [], ri = -1;
5018         tagName = tagName.toLowerCase();
5019         for(var i = 0, ci; ci = cs[i]; i++){
5020             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byId(cs, attr, id){
5028         if(cs.tagName || cs == document){
5029             cs = [cs];
5030         }
5031         if(!id){
5032             return cs;
5033         }
5034         var r = [], ri = -1;
5035         for(var i = 0,ci; ci = cs[i]; i++){
5036             if(ci && ci.id == id){
5037                 r[++ri] = ci;
5038                 return r;
5039             }
5040         }
5041         return r;
5042     };
5043
5044     function byAttribute(cs, attr, value, op, custom){
5045         var r = [], ri = -1, st = custom=="{";
5046         var f = Roo.DomQuery.operators[op];
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             var a;
5049             if(st){
5050                 a = Roo.DomQuery.getStyle(ci, attr);
5051             }
5052             else if(attr == "class" || attr == "className"){
5053                 a = ci.className;
5054             }else if(attr == "for"){
5055                 a = ci.htmlFor;
5056             }else if(attr == "href"){
5057                 a = ci.getAttribute("href", 2);
5058             }else{
5059                 a = ci.getAttribute(attr);
5060             }
5061             if((f && f(a, value)) || (!f && a)){
5062                 r[++ri] = ci;
5063             }
5064         }
5065         return r;
5066     };
5067
5068     function byPseudo(cs, name, value){
5069         return Roo.DomQuery.pseudos[name](cs, value);
5070     };
5071
5072     // This is for IE MSXML which does not support expandos.
5073     // IE runs the same speed using setAttribute, however FF slows way down
5074     // and Safari completely fails so they need to continue to use expandos.
5075     var isIE = window.ActiveXObject ? true : false;
5076
5077     // this eval is stop the compressor from
5078     // renaming the variable to something shorter
5079     
5080     /** eval:var:batch */
5081     var batch = 30803; 
5082
5083     var key = 30803;
5084
5085     function nodupIEXml(cs){
5086         var d = ++key;
5087         cs[0].setAttribute("_nodup", d);
5088         var r = [cs[0]];
5089         for(var i = 1, len = cs.length; i < len; i++){
5090             var c = cs[i];
5091             if(!c.getAttribute("_nodup") != d){
5092                 c.setAttribute("_nodup", d);
5093                 r[r.length] = c;
5094             }
5095         }
5096         for(var i = 0, len = cs.length; i < len; i++){
5097             cs[i].removeAttribute("_nodup");
5098         }
5099         return r;
5100     }
5101
5102     function nodup(cs){
5103         if(!cs){
5104             return [];
5105         }
5106         var len = cs.length, c, i, r = cs, cj, ri = -1;
5107         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5108             return cs;
5109         }
5110         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5111             return nodupIEXml(cs);
5112         }
5113         var d = ++key;
5114         cs[0]._nodup = d;
5115         for(i = 1; c = cs[i]; i++){
5116             if(c._nodup != d){
5117                 c._nodup = d;
5118             }else{
5119                 r = [];
5120                 for(var j = 0; j < i; j++){
5121                     r[++ri] = cs[j];
5122                 }
5123                 for(j = i+1; cj = cs[j]; j++){
5124                     if(cj._nodup != d){
5125                         cj._nodup = d;
5126                         r[++ri] = cj;
5127                     }
5128                 }
5129                 return r;
5130             }
5131         }
5132         return r;
5133     }
5134
5135     function quickDiffIEXml(c1, c2){
5136         var d = ++key;
5137         for(var i = 0, len = c1.length; i < len; i++){
5138             c1[i].setAttribute("_qdiff", d);
5139         }
5140         var r = [];
5141         for(var i = 0, len = c2.length; i < len; i++){
5142             if(c2[i].getAttribute("_qdiff") != d){
5143                 r[r.length] = c2[i];
5144             }
5145         }
5146         for(var i = 0, len = c1.length; i < len; i++){
5147            c1[i].removeAttribute("_qdiff");
5148         }
5149         return r;
5150     }
5151
5152     function quickDiff(c1, c2){
5153         var len1 = c1.length;
5154         if(!len1){
5155             return c2;
5156         }
5157         if(isIE && c1[0].selectSingleNode){
5158             return quickDiffIEXml(c1, c2);
5159         }
5160         var d = ++key;
5161         for(var i = 0; i < len1; i++){
5162             c1[i]._qdiff = d;
5163         }
5164         var r = [];
5165         for(var i = 0, len = c2.length; i < len; i++){
5166             if(c2[i]._qdiff != d){
5167                 r[r.length] = c2[i];
5168             }
5169         }
5170         return r;
5171     }
5172
5173     function quickId(ns, mode, root, id){
5174         if(ns == root){
5175            var d = root.ownerDocument || root;
5176            return d.getElementById(id);
5177         }
5178         ns = getNodes(ns, mode, "*");
5179         return byId(ns, null, id);
5180     }
5181
5182     return {
5183         getStyle : function(el, name){
5184             return Roo.fly(el).getStyle(name);
5185         },
5186         /**
5187          * Compiles a selector/xpath query into a reusable function. The returned function
5188          * takes one parameter "root" (optional), which is the context node from where the query should start.
5189          * @param {String} selector The selector/xpath query
5190          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5191          * @return {Function}
5192          */
5193         compile : function(path, type){
5194             type = type || "select";
5195             
5196             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5197             var q = path, mode, lq;
5198             var tk = Roo.DomQuery.matchers;
5199             var tklen = tk.length;
5200             var mm;
5201
5202             // accept leading mode switch
5203             var lmode = q.match(modeRe);
5204             if(lmode && lmode[1]){
5205                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5206                 q = q.replace(lmode[1], "");
5207             }
5208             // strip leading slashes
5209             while(path.substr(0, 1)=="/"){
5210                 path = path.substr(1);
5211             }
5212
5213             while(q && lq != q){
5214                 lq = q;
5215                 var tm = q.match(tagTokenRe);
5216                 if(type == "select"){
5217                     if(tm){
5218                         if(tm[1] == "#"){
5219                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5220                         }else{
5221                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5222                         }
5223                         q = q.replace(tm[0], "");
5224                     }else if(q.substr(0, 1) != '@'){
5225                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5226                     }
5227                 }else{
5228                     if(tm){
5229                         if(tm[1] == "#"){
5230                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5231                         }else{
5232                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5233                         }
5234                         q = q.replace(tm[0], "");
5235                     }
5236                 }
5237                 while(!(mm = q.match(modeRe))){
5238                     var matched = false;
5239                     for(var j = 0; j < tklen; j++){
5240                         var t = tk[j];
5241                         var m = q.match(t.re);
5242                         if(m){
5243                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5244                                                     return m[i];
5245                                                 });
5246                             q = q.replace(m[0], "");
5247                             matched = true;
5248                             break;
5249                         }
5250                     }
5251                     // prevent infinite loop on bad selector
5252                     if(!matched){
5253                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5254                     }
5255                 }
5256                 if(mm[1]){
5257                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5258                     q = q.replace(mm[1], "");
5259                 }
5260             }
5261             fn[fn.length] = "return nodup(n);\n}";
5262             
5263              /** 
5264               * list of variables that need from compression as they are used by eval.
5265              *  eval:var:batch 
5266              *  eval:var:nodup
5267              *  eval:var:byTag
5268              *  eval:var:ById
5269              *  eval:var:getNodes
5270              *  eval:var:quickId
5271              *  eval:var:mode
5272              *  eval:var:root
5273              *  eval:var:n
5274              *  eval:var:byClassName
5275              *  eval:var:byPseudo
5276              *  eval:var:byAttribute
5277              *  eval:var:attrValue
5278              * 
5279              **/ 
5280             eval(fn.join(""));
5281             return f;
5282         },
5283
5284         /**
5285          * Selects a group of elements.
5286          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5287          * @param {Node} root (optional) The start of the query (defaults to document).
5288          * @return {Array}
5289          */
5290         select : function(path, root, type){
5291             if(!root || root == document){
5292                 root = document;
5293             }
5294             if(typeof root == "string"){
5295                 root = document.getElementById(root);
5296             }
5297             var paths = path.split(",");
5298             var results = [];
5299             for(var i = 0, len = paths.length; i < len; i++){
5300                 var p = paths[i].replace(trimRe, "");
5301                 if(!cache[p]){
5302                     cache[p] = Roo.DomQuery.compile(p);
5303                     if(!cache[p]){
5304                         throw p + " is not a valid selector";
5305                     }
5306                 }
5307                 var result = cache[p](root);
5308                 if(result && result != document){
5309                     results = results.concat(result);
5310                 }
5311             }
5312             if(paths.length > 1){
5313                 return nodup(results);
5314             }
5315             return results;
5316         },
5317
5318         /**
5319          * Selects a single element.
5320          * @param {String} selector The selector/xpath query
5321          * @param {Node} root (optional) The start of the query (defaults to document).
5322          * @return {Element}
5323          */
5324         selectNode : function(path, root){
5325             return Roo.DomQuery.select(path, root)[0];
5326         },
5327
5328         /**
5329          * Selects the value of a node, optionally replacing null with the defaultValue.
5330          * @param {String} selector The selector/xpath query
5331          * @param {Node} root (optional) The start of the query (defaults to document).
5332          * @param {String} defaultValue
5333          */
5334         selectValue : function(path, root, defaultValue){
5335             path = path.replace(trimRe, "");
5336             if(!valueCache[path]){
5337                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5338             }
5339             var n = valueCache[path](root);
5340             n = n[0] ? n[0] : n;
5341             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5342             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5343         },
5344
5345         /**
5346          * Selects the value of a node, parsing integers and floats.
5347          * @param {String} selector The selector/xpath query
5348          * @param {Node} root (optional) The start of the query (defaults to document).
5349          * @param {Number} defaultValue
5350          * @return {Number}
5351          */
5352         selectNumber : function(path, root, defaultValue){
5353             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5354             return parseFloat(v);
5355         },
5356
5357         /**
5358          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5359          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5360          * @param {String} selector The simple selector to test
5361          * @return {Boolean}
5362          */
5363         is : function(el, ss){
5364             if(typeof el == "string"){
5365                 el = document.getElementById(el);
5366             }
5367             var isArray = (el instanceof Array);
5368             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5369             return isArray ? (result.length == el.length) : (result.length > 0);
5370         },
5371
5372         /**
5373          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5374          * @param {Array} el An array of elements to filter
5375          * @param {String} selector The simple selector to test
5376          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5377          * the selector instead of the ones that match
5378          * @return {Array}
5379          */
5380         filter : function(els, ss, nonMatches){
5381             ss = ss.replace(trimRe, "");
5382             if(!simpleCache[ss]){
5383                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5384             }
5385             var result = simpleCache[ss](els);
5386             return nonMatches ? quickDiff(result, els) : result;
5387         },
5388
5389         /**
5390          * Collection of matching regular expressions and code snippets.
5391          */
5392         matchers : [{
5393                 re: /^\.([\w-]+)/,
5394                 select: 'n = byClassName(n, null, " {1} ");'
5395             }, {
5396                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5397                 select: 'n = byPseudo(n, "{1}", "{2}");'
5398             },{
5399                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5400                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5401             }, {
5402                 re: /^#([\w-]+)/,
5403                 select: 'n = byId(n, null, "{1}");'
5404             },{
5405                 re: /^@([\w-]+)/,
5406                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5407             }
5408         ],
5409
5410         /**
5411          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5412          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5413          */
5414         operators : {
5415             "=" : function(a, v){
5416                 return a == v;
5417             },
5418             "!=" : function(a, v){
5419                 return a != v;
5420             },
5421             "^=" : function(a, v){
5422                 return a && a.substr(0, v.length) == v;
5423             },
5424             "$=" : function(a, v){
5425                 return a && a.substr(a.length-v.length) == v;
5426             },
5427             "*=" : function(a, v){
5428                 return a && a.indexOf(v) !== -1;
5429             },
5430             "%=" : function(a, v){
5431                 return (a % v) == 0;
5432             },
5433             "|=" : function(a, v){
5434                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5435             },
5436             "~=" : function(a, v){
5437                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5438             }
5439         },
5440
5441         /**
5442          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5443          * and the argument (if any) supplied in the selector.
5444          */
5445         pseudos : {
5446             "first-child" : function(c){
5447                 var r = [], ri = -1, n;
5448                 for(var i = 0, ci; ci = n = c[i]; i++){
5449                     while((n = n.previousSibling) && n.nodeType != 1);
5450                     if(!n){
5451                         r[++ri] = ci;
5452                     }
5453                 }
5454                 return r;
5455             },
5456
5457             "last-child" : function(c){
5458                 var r = [], ri = -1, n;
5459                 for(var i = 0, ci; ci = n = c[i]; i++){
5460                     while((n = n.nextSibling) && n.nodeType != 1);
5461                     if(!n){
5462                         r[++ri] = ci;
5463                     }
5464                 }
5465                 return r;
5466             },
5467
5468             "nth-child" : function(c, a) {
5469                 var r = [], ri = -1;
5470                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5471                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5472                 for(var i = 0, n; n = c[i]; i++){
5473                     var pn = n.parentNode;
5474                     if (batch != pn._batch) {
5475                         var j = 0;
5476                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5477                             if(cn.nodeType == 1){
5478                                cn.nodeIndex = ++j;
5479                             }
5480                         }
5481                         pn._batch = batch;
5482                     }
5483                     if (f == 1) {
5484                         if (l == 0 || n.nodeIndex == l){
5485                             r[++ri] = n;
5486                         }
5487                     } else if ((n.nodeIndex + l) % f == 0){
5488                         r[++ri] = n;
5489                     }
5490                 }
5491
5492                 return r;
5493             },
5494
5495             "only-child" : function(c){
5496                 var r = [], ri = -1;;
5497                 for(var i = 0, ci; ci = c[i]; i++){
5498                     if(!prev(ci) && !next(ci)){
5499                         r[++ri] = ci;
5500                     }
5501                 }
5502                 return r;
5503             },
5504
5505             "empty" : function(c){
5506                 var r = [], ri = -1;
5507                 for(var i = 0, ci; ci = c[i]; i++){
5508                     var cns = ci.childNodes, j = 0, cn, empty = true;
5509                     while(cn = cns[j]){
5510                         ++j;
5511                         if(cn.nodeType == 1 || cn.nodeType == 3){
5512                             empty = false;
5513                             break;
5514                         }
5515                     }
5516                     if(empty){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             },
5522
5523             "contains" : function(c, v){
5524                 var r = [], ri = -1;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "nodeValue" : function(c, v){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5537                         r[++ri] = ci;
5538                     }
5539                 }
5540                 return r;
5541             },
5542
5543             "checked" : function(c){
5544                 var r = [], ri = -1;
5545                 for(var i = 0, ci; ci = c[i]; i++){
5546                     if(ci.checked == true){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "not" : function(c, ss){
5554                 return Roo.DomQuery.filter(c, ss, true);
5555             },
5556
5557             "odd" : function(c){
5558                 return this["nth-child"](c, "odd");
5559             },
5560
5561             "even" : function(c){
5562                 return this["nth-child"](c, "even");
5563             },
5564
5565             "nth" : function(c, a){
5566                 return c[a-1] || [];
5567             },
5568
5569             "first" : function(c){
5570                 return c[0] || [];
5571             },
5572
5573             "last" : function(c){
5574                 return c[c.length-1] || [];
5575             },
5576
5577             "has" : function(c, ss){
5578                 var s = Roo.DomQuery.select;
5579                 var r = [], ri = -1;
5580                 for(var i = 0, ci; ci = c[i]; i++){
5581                     if(s(ss, ci).length > 0){
5582                         r[++ri] = ci;
5583                     }
5584                 }
5585                 return r;
5586             },
5587
5588             "next" : function(c, ss){
5589                 var is = Roo.DomQuery.is;
5590                 var r = [], ri = -1;
5591                 for(var i = 0, ci; ci = c[i]; i++){
5592                     var n = next(ci);
5593                     if(n && is(n, ss)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "prev" : function(c, ss){
5601                 var is = Roo.DomQuery.is;
5602                 var r = [], ri = -1;
5603                 for(var i = 0, ci; ci = c[i]; i++){
5604                     var n = prev(ci);
5605                     if(n && is(n, ss)){
5606                         r[++ri] = ci;
5607                     }
5608                 }
5609                 return r;
5610             }
5611         }
5612     };
5613 }();
5614
5615 /**
5616  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5617  * @param {String} path The selector/xpath query
5618  * @param {Node} root (optional) The start of the query (defaults to document).
5619  * @return {Array}
5620  * @member Roo
5621  * @method query
5622  */
5623 Roo.query = Roo.DomQuery.select;
5624 /*
5625  * Based on:
5626  * Ext JS Library 1.1.1
5627  * Copyright(c) 2006-2007, Ext JS, LLC.
5628  *
5629  * Originally Released Under LGPL - original licence link has changed is not relivant.
5630  *
5631  * Fork - LGPL
5632  * <script type="text/javascript">
5633  */
5634
5635 /**
5636  * @class Roo.util.Observable
5637  * Base class that provides a common interface for publishing events. Subclasses are expected to
5638  * to have a property "events" with all the events defined.<br>
5639  * For example:
5640  * <pre><code>
5641  Employee = function(name){
5642     this.name = name;
5643     this.addEvents({
5644         "fired" : true,
5645         "quit" : true
5646     });
5647  }
5648  Roo.extend(Employee, Roo.util.Observable);
5649 </code></pre>
5650  * @param {Object} config properties to use (incuding events / listeners)
5651  */
5652
5653 Roo.util.Observable = function(cfg){
5654     
5655     cfg = cfg|| {};
5656     this.addEvents(cfg.events || {});
5657     if (cfg.events) {
5658         delete cfg.events; // make sure
5659     }
5660      
5661     Roo.apply(this, cfg);
5662     
5663     if(this.listeners){
5664         this.on(this.listeners);
5665         delete this.listeners;
5666     }
5667 };
5668 Roo.util.Observable.prototype = {
5669     /** 
5670  * @cfg {Object} listeners  list of events and functions to call for this object, 
5671  * For example :
5672  * <pre><code>
5673     listeners :  { 
5674        'click' : function(e) {
5675            ..... 
5676         } ,
5677         .... 
5678     } 
5679   </code></pre>
5680  */
5681     
5682     
5683     /**
5684      * Fires the specified event with the passed parameters (minus the event name).
5685      * @param {String} eventName
5686      * @param {Object...} args Variable number of parameters are passed to handlers
5687      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5688      */
5689     fireEvent : function(){
5690         var ce = this.events[arguments[0].toLowerCase()];
5691         if(typeof ce == "object"){
5692             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5693         }else{
5694             return true;
5695         }
5696     },
5697
5698     // private
5699     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5700
5701     /**
5702      * Appends an event handler to this component
5703      * @param {String}   eventName The type of event to listen for
5704      * @param {Function} handler The method the event invokes
5705      * @param {Object}   scope (optional) The scope in which to execute the handler
5706      * function. The handler function's "this" context.
5707      * @param {Object}   options (optional) An object containing handler configuration
5708      * properties. This may contain any of the following properties:<ul>
5709      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5710      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5711      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5712      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5713      * by the specified number of milliseconds. If the event fires again within that time, the original
5714      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5715      * </ul><br>
5716      * <p>
5717      * <b>Combining Options</b><br>
5718      * Using the options argument, it is possible to combine different types of listeners:<br>
5719      * <br>
5720      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5721                 <pre><code>
5722                 el.on('click', this.onClick, this, {
5723                         single: true,
5724                 delay: 100,
5725                 forumId: 4
5726                 });
5727                 </code></pre>
5728      * <p>
5729      * <b>Attaching multiple handlers in 1 call</b><br>
5730      * The method also allows for a single argument to be passed which is a config object containing properties
5731      * which specify multiple handlers.
5732      * <pre><code>
5733                 el.on({
5734                         'click': {
5735                         fn: this.onClick,
5736                         scope: this,
5737                         delay: 100
5738                 }, 
5739                 'mouseover': {
5740                         fn: this.onMouseOver,
5741                         scope: this
5742                 },
5743                 'mouseout': {
5744                         fn: this.onMouseOut,
5745                         scope: this
5746                 }
5747                 });
5748                 </code></pre>
5749      * <p>
5750      * Or a shorthand syntax which passes the same scope object to all handlers:
5751         <pre><code>
5752                 el.on({
5753                         'click': this.onClick,
5754                 'mouseover': this.onMouseOver,
5755                 'mouseout': this.onMouseOut,
5756                 scope: this
5757                 });
5758                 </code></pre>
5759      */
5760     addListener : function(eventName, fn, scope, o){
5761         if(typeof eventName == "object"){
5762             o = eventName;
5763             for(var e in o){
5764                 if(this.filterOptRe.test(e)){
5765                     continue;
5766                 }
5767                 if(typeof o[e] == "function"){
5768                     // shared options
5769                     this.addListener(e, o[e], o.scope,  o);
5770                 }else{
5771                     // individual options
5772                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5773                 }
5774             }
5775             return;
5776         }
5777         o = (!o || typeof o == "boolean") ? {} : o;
5778         eventName = eventName.toLowerCase();
5779         var ce = this.events[eventName] || true;
5780         if(typeof ce == "boolean"){
5781             ce = new Roo.util.Event(this, eventName);
5782             this.events[eventName] = ce;
5783         }
5784         ce.addListener(fn, scope, o);
5785     },
5786
5787     /**
5788      * Removes a listener
5789      * @param {String}   eventName     The type of event to listen for
5790      * @param {Function} handler        The handler to remove
5791      * @param {Object}   scope  (optional) The scope (this object) for the handler
5792      */
5793     removeListener : function(eventName, fn, scope){
5794         var ce = this.events[eventName.toLowerCase()];
5795         if(typeof ce == "object"){
5796             ce.removeListener(fn, scope);
5797         }
5798     },
5799
5800     /**
5801      * Removes all listeners for this object
5802      */
5803     purgeListeners : function(){
5804         for(var evt in this.events){
5805             if(typeof this.events[evt] == "object"){
5806                  this.events[evt].clearListeners();
5807             }
5808         }
5809     },
5810
5811     relayEvents : function(o, events){
5812         var createHandler = function(ename){
5813             return function(){
5814                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5815             };
5816         };
5817         for(var i = 0, len = events.length; i < len; i++){
5818             var ename = events[i];
5819             if(!this.events[ename]){ this.events[ename] = true; };
5820             o.on(ename, createHandler(ename), this);
5821         }
5822     },
5823
5824     /**
5825      * Used to define events on this Observable
5826      * @param {Object} object The object with the events defined
5827      */
5828     addEvents : function(o){
5829         if(!this.events){
5830             this.events = {};
5831         }
5832         Roo.applyIf(this.events, o);
5833     },
5834
5835     /**
5836      * Checks to see if this object has any listeners for a specified event
5837      * @param {String} eventName The name of the event to check for
5838      * @return {Boolean} True if the event is being listened for, else false
5839      */
5840     hasListener : function(eventName){
5841         var e = this.events[eventName];
5842         return typeof e == "object" && e.listeners.length > 0;
5843     }
5844 };
5845 /**
5846  * Appends an event handler to this element (shorthand for addListener)
5847  * @param {String}   eventName     The type of event to listen for
5848  * @param {Function} handler        The method the event invokes
5849  * @param {Object}   scope (optional) The scope in which to execute the handler
5850  * function. The handler function's "this" context.
5851  * @param {Object}   options  (optional)
5852  * @method
5853  */
5854 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5855 /**
5856  * Removes a listener (shorthand for removeListener)
5857  * @param {String}   eventName     The type of event to listen for
5858  * @param {Function} handler        The handler to remove
5859  * @param {Object}   scope  (optional) The scope (this object) for the handler
5860  * @method
5861  */
5862 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5863
5864 /**
5865  * Starts capture on the specified Observable. All events will be passed
5866  * to the supplied function with the event name + standard signature of the event
5867  * <b>before</b> the event is fired. If the supplied function returns false,
5868  * the event will not fire.
5869  * @param {Observable} o The Observable to capture
5870  * @param {Function} fn The function to call
5871  * @param {Object} scope (optional) The scope (this object) for the fn
5872  * @static
5873  */
5874 Roo.util.Observable.capture = function(o, fn, scope){
5875     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5876 };
5877
5878 /**
5879  * Removes <b>all</b> added captures from the Observable.
5880  * @param {Observable} o The Observable to release
5881  * @static
5882  */
5883 Roo.util.Observable.releaseCapture = function(o){
5884     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5885 };
5886
5887 (function(){
5888
5889     var createBuffered = function(h, o, scope){
5890         var task = new Roo.util.DelayedTask();
5891         return function(){
5892             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5893         };
5894     };
5895
5896     var createSingle = function(h, e, fn, scope){
5897         return function(){
5898             e.removeListener(fn, scope);
5899             return h.apply(scope, arguments);
5900         };
5901     };
5902
5903     var createDelayed = function(h, o, scope){
5904         return function(){
5905             var args = Array.prototype.slice.call(arguments, 0);
5906             setTimeout(function(){
5907                 h.apply(scope, args);
5908             }, o.delay || 10);
5909         };
5910     };
5911
5912     Roo.util.Event = function(obj, name){
5913         this.name = name;
5914         this.obj = obj;
5915         this.listeners = [];
5916     };
5917
5918     Roo.util.Event.prototype = {
5919         addListener : function(fn, scope, options){
5920             var o = options || {};
5921             scope = scope || this.obj;
5922             if(!this.isListening(fn, scope)){
5923                 var l = {fn: fn, scope: scope, options: o};
5924                 var h = fn;
5925                 if(o.delay){
5926                     h = createDelayed(h, o, scope);
5927                 }
5928                 if(o.single){
5929                     h = createSingle(h, this, fn, scope);
5930                 }
5931                 if(o.buffer){
5932                     h = createBuffered(h, o, scope);
5933                 }
5934                 l.fireFn = h;
5935                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5936                     this.listeners.push(l);
5937                 }else{
5938                     this.listeners = this.listeners.slice(0);
5939                     this.listeners.push(l);
5940                 }
5941             }
5942         },
5943
5944         findListener : function(fn, scope){
5945             scope = scope || this.obj;
5946             var ls = this.listeners;
5947             for(var i = 0, len = ls.length; i < len; i++){
5948                 var l = ls[i];
5949                 if(l.fn == fn && l.scope == scope){
5950                     return i;
5951                 }
5952             }
5953             return -1;
5954         },
5955
5956         isListening : function(fn, scope){
5957             return this.findListener(fn, scope) != -1;
5958         },
5959
5960         removeListener : function(fn, scope){
5961             var index;
5962             if((index = this.findListener(fn, scope)) != -1){
5963                 if(!this.firing){
5964                     this.listeners.splice(index, 1);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.splice(index, 1);
5968                 }
5969                 return true;
5970             }
5971             return false;
5972         },
5973
5974         clearListeners : function(){
5975             this.listeners = [];
5976         },
5977
5978         fire : function(){
5979             var ls = this.listeners, scope, len = ls.length;
5980             if(len > 0){
5981                 this.firing = true;
5982                 var args = Array.prototype.slice.call(arguments, 0);
5983                 for(var i = 0; i < len; i++){
5984                     var l = ls[i];
5985                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5986                         this.firing = false;
5987                         return false;
5988                     }
5989                 }
5990                 this.firing = false;
5991             }
5992             return true;
5993         }
5994     };
5995 })();/*
5996  * Based on:
5997  * Ext JS Library 1.1.1
5998  * Copyright(c) 2006-2007, Ext JS, LLC.
5999  *
6000  * Originally Released Under LGPL - original licence link has changed is not relivant.
6001  *
6002  * Fork - LGPL
6003  * <script type="text/javascript">
6004  */
6005
6006 /**
6007  * @class Roo.EventManager
6008  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6009  * several useful events directly.
6010  * See {@link Roo.EventObject} for more details on normalized event objects.
6011  * @singleton
6012  */
6013 Roo.EventManager = function(){
6014     var docReadyEvent, docReadyProcId, docReadyState = false;
6015     var resizeEvent, resizeTask, textEvent, textSize;
6016     var E = Roo.lib.Event;
6017     var D = Roo.lib.Dom;
6018
6019
6020     var fireDocReady = function(){
6021         if(!docReadyState){
6022             docReadyState = true;
6023             Roo.isReady = true;
6024             if(docReadyProcId){
6025                 clearInterval(docReadyProcId);
6026             }
6027             if(Roo.isGecko || Roo.isOpera) {
6028                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6029             }
6030             if(Roo.isIE){
6031                 var defer = document.getElementById("ie-deferred-loader");
6032                 if(defer){
6033                     defer.onreadystatechange = null;
6034                     defer.parentNode.removeChild(defer);
6035                 }
6036             }
6037             if(docReadyEvent){
6038                 docReadyEvent.fire();
6039                 docReadyEvent.clearListeners();
6040             }
6041         }
6042     };
6043     
6044     var initDocReady = function(){
6045         docReadyEvent = new Roo.util.Event();
6046         if(Roo.isGecko || Roo.isOpera) {
6047             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6048         }else if(Roo.isIE){
6049             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6050             var defer = document.getElementById("ie-deferred-loader");
6051             defer.onreadystatechange = function(){
6052                 if(this.readyState == "complete"){
6053                     fireDocReady();
6054                 }
6055             };
6056         }else if(Roo.isSafari){ 
6057             docReadyProcId = setInterval(function(){
6058                 var rs = document.readyState;
6059                 if(rs == "complete") {
6060                     fireDocReady();     
6061                  }
6062             }, 10);
6063         }
6064         // no matter what, make sure it fires on load
6065         E.on(window, "load", fireDocReady);
6066     };
6067
6068     var createBuffered = function(h, o){
6069         var task = new Roo.util.DelayedTask(h);
6070         return function(e){
6071             // create new event object impl so new events don't wipe out properties
6072             e = new Roo.EventObjectImpl(e);
6073             task.delay(o.buffer, h, null, [e]);
6074         };
6075     };
6076
6077     var createSingle = function(h, el, ename, fn){
6078         return function(e){
6079             Roo.EventManager.removeListener(el, ename, fn);
6080             h(e);
6081         };
6082     };
6083
6084     var createDelayed = function(h, o){
6085         return function(e){
6086             // create new event object impl so new events don't wipe out properties
6087             e = new Roo.EventObjectImpl(e);
6088             setTimeout(function(){
6089                 h(e);
6090             }, o.delay || 10);
6091         };
6092     };
6093
6094     var listen = function(element, ename, opt, fn, scope){
6095         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6096         fn = fn || o.fn; scope = scope || o.scope;
6097         var el = Roo.getDom(element);
6098         if(!el){
6099             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6100         }
6101         var h = function(e){
6102             e = Roo.EventObject.setEvent(e);
6103             var t;
6104             if(o.delegate){
6105                 t = e.getTarget(o.delegate, el);
6106                 if(!t){
6107                     return;
6108                 }
6109             }else{
6110                 t = e.target;
6111             }
6112             if(o.stopEvent === true){
6113                 e.stopEvent();
6114             }
6115             if(o.preventDefault === true){
6116                e.preventDefault();
6117             }
6118             if(o.stopPropagation === true){
6119                 e.stopPropagation();
6120             }
6121
6122             if(o.normalized === false){
6123                 e = e.browserEvent;
6124             }
6125
6126             fn.call(scope || el, e, t, o);
6127         };
6128         if(o.delay){
6129             h = createDelayed(h, o);
6130         }
6131         if(o.single){
6132             h = createSingle(h, el, ename, fn);
6133         }
6134         if(o.buffer){
6135             h = createBuffered(h, o);
6136         }
6137         fn._handlers = fn._handlers || [];
6138         fn._handlers.push([Roo.id(el), ename, h]);
6139
6140         E.on(el, ename, h);
6141         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6142             el.addEventListener("DOMMouseScroll", h, false);
6143             E.on(window, 'unload', function(){
6144                 el.removeEventListener("DOMMouseScroll", h, false);
6145             });
6146         }
6147         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6148             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6149         }
6150         return h;
6151     };
6152
6153     var stopListening = function(el, ename, fn){
6154         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6155         if(hds){
6156             for(var i = 0, len = hds.length; i < len; i++){
6157                 var h = hds[i];
6158                 if(h[0] == id && h[1] == ename){
6159                     hd = h[2];
6160                     hds.splice(i, 1);
6161                     break;
6162                 }
6163             }
6164         }
6165         E.un(el, ename, hd);
6166         el = Roo.getDom(el);
6167         if(ename == "mousewheel" && el.addEventListener){
6168             el.removeEventListener("DOMMouseScroll", hd, false);
6169         }
6170         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6171             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6172         }
6173     };
6174
6175     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6176     
6177     var pub = {
6178         
6179         
6180         /** 
6181          * Fix for doc tools
6182          * @scope Roo.EventManager
6183          */
6184         
6185         
6186         /** 
6187          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6188          * object with a Roo.EventObject
6189          * @param {Function} fn        The method the event invokes
6190          * @param {Object}   scope    An object that becomes the scope of the handler
6191          * @param {boolean}  override If true, the obj passed in becomes
6192          *                             the execution scope of the listener
6193          * @return {Function} The wrapped function
6194          * @deprecated
6195          */
6196         wrap : function(fn, scope, override){
6197             return function(e){
6198                 Roo.EventObject.setEvent(e);
6199                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6200             };
6201         },
6202         
6203         /**
6204      * Appends an event handler to an element (shorthand for addListener)
6205      * @param {String/HTMLElement}   element        The html element or id to assign the
6206      * @param {String}   eventName The type of event to listen for
6207      * @param {Function} handler The method the event invokes
6208      * @param {Object}   scope (optional) The scope in which to execute the handler
6209      * function. The handler function's "this" context.
6210      * @param {Object}   options (optional) An object containing handler configuration
6211      * properties. This may contain any of the following properties:<ul>
6212      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6213      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6214      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6215      * <li>preventDefault {Boolean} True to prevent the default action</li>
6216      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6217      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6218      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6219      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6220      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6221      * by the specified number of milliseconds. If the event fires again within that time, the original
6222      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6223      * </ul><br>
6224      * <p>
6225      * <b>Combining Options</b><br>
6226      * Using the options argument, it is possible to combine different types of listeners:<br>
6227      * <br>
6228      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6229      * Code:<pre><code>
6230 el.on('click', this.onClick, this, {
6231     single: true,
6232     delay: 100,
6233     stopEvent : true,
6234     forumId: 4
6235 });</code></pre>
6236      * <p>
6237      * <b>Attaching multiple handlers in 1 call</b><br>
6238       * The method also allows for a single argument to be passed which is a config object containing properties
6239      * which specify multiple handlers.
6240      * <p>
6241      * Code:<pre><code>
6242 el.on({
6243     'click' : {
6244         fn: this.onClick
6245         scope: this,
6246         delay: 100
6247     },
6248     'mouseover' : {
6249         fn: this.onMouseOver
6250         scope: this
6251     },
6252     'mouseout' : {
6253         fn: this.onMouseOut
6254         scope: this
6255     }
6256 });</code></pre>
6257      * <p>
6258      * Or a shorthand syntax:<br>
6259      * Code:<pre><code>
6260 el.on({
6261     'click' : this.onClick,
6262     'mouseover' : this.onMouseOver,
6263     'mouseout' : this.onMouseOut
6264     scope: this
6265 });</code></pre>
6266      */
6267         addListener : function(element, eventName, fn, scope, options){
6268             if(typeof eventName == "object"){
6269                 var o = eventName;
6270                 for(var e in o){
6271                     if(propRe.test(e)){
6272                         continue;
6273                     }
6274                     if(typeof o[e] == "function"){
6275                         // shared options
6276                         listen(element, e, o, o[e], o.scope);
6277                     }else{
6278                         // individual options
6279                         listen(element, e, o[e]);
6280                     }
6281                 }
6282                 return;
6283             }
6284             return listen(element, eventName, options, fn, scope);
6285         },
6286         
6287         /**
6288          * Removes an event handler
6289          *
6290          * @param {String/HTMLElement}   element        The id or html element to remove the 
6291          *                             event from
6292          * @param {String}   eventName     The type of event
6293          * @param {Function} fn
6294          * @return {Boolean} True if a listener was actually removed
6295          */
6296         removeListener : function(element, eventName, fn){
6297             return stopListening(element, eventName, fn);
6298         },
6299         
6300         /**
6301          * Fires when the document is ready (before onload and before images are loaded). Can be 
6302          * accessed shorthanded Roo.onReady().
6303          * @param {Function} fn        The method the event invokes
6304          * @param {Object}   scope    An  object that becomes the scope of the handler
6305          * @param {boolean}  options
6306          */
6307         onDocumentReady : function(fn, scope, options){
6308             if(docReadyState){ // if it already fired
6309                 docReadyEvent.addListener(fn, scope, options);
6310                 docReadyEvent.fire();
6311                 docReadyEvent.clearListeners();
6312                 return;
6313             }
6314             if(!docReadyEvent){
6315                 initDocReady();
6316             }
6317             docReadyEvent.addListener(fn, scope, options);
6318         },
6319         
6320         /**
6321          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6322          * @param {Function} fn        The method the event invokes
6323          * @param {Object}   scope    An object that becomes the scope of the handler
6324          * @param {boolean}  options
6325          */
6326         onWindowResize : function(fn, scope, options){
6327             if(!resizeEvent){
6328                 resizeEvent = new Roo.util.Event();
6329                 resizeTask = new Roo.util.DelayedTask(function(){
6330                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6331                 });
6332                 E.on(window, "resize", function(){
6333                     if(Roo.isIE){
6334                         resizeTask.delay(50);
6335                     }else{
6336                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6337                     }
6338                 });
6339             }
6340             resizeEvent.addListener(fn, scope, options);
6341         },
6342
6343         /**
6344          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6345          * @param {Function} fn        The method the event invokes
6346          * @param {Object}   scope    An object that becomes the scope of the handler
6347          * @param {boolean}  options
6348          */
6349         onTextResize : function(fn, scope, options){
6350             if(!textEvent){
6351                 textEvent = new Roo.util.Event();
6352                 var textEl = new Roo.Element(document.createElement('div'));
6353                 textEl.dom.className = 'x-text-resize';
6354                 textEl.dom.innerHTML = 'X';
6355                 textEl.appendTo(document.body);
6356                 textSize = textEl.dom.offsetHeight;
6357                 setInterval(function(){
6358                     if(textEl.dom.offsetHeight != textSize){
6359                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6360                     }
6361                 }, this.textResizeInterval);
6362             }
6363             textEvent.addListener(fn, scope, options);
6364         },
6365
6366         /**
6367          * Removes the passed window resize listener.
6368          * @param {Function} fn        The method the event invokes
6369          * @param {Object}   scope    The scope of handler
6370          */
6371         removeResizeListener : function(fn, scope){
6372             if(resizeEvent){
6373                 resizeEvent.removeListener(fn, scope);
6374             }
6375         },
6376
6377         // private
6378         fireResize : function(){
6379             if(resizeEvent){
6380                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6381             }   
6382         },
6383         /**
6384          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6385          */
6386         ieDeferSrc : false,
6387         /**
6388          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6389          */
6390         textResizeInterval : 50
6391     };
6392     
6393     /**
6394      * Fix for doc tools
6395      * @scopeAlias pub=Roo.EventManager
6396      */
6397     
6398      /**
6399      * Appends an event handler to an element (shorthand for addListener)
6400      * @param {String/HTMLElement}   element        The html element or id to assign the
6401      * @param {String}   eventName The type of event to listen for
6402      * @param {Function} handler The method the event invokes
6403      * @param {Object}   scope (optional) The scope in which to execute the handler
6404      * function. The handler function's "this" context.
6405      * @param {Object}   options (optional) An object containing handler configuration
6406      * properties. This may contain any of the following properties:<ul>
6407      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6408      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6409      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6410      * <li>preventDefault {Boolean} True to prevent the default action</li>
6411      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6412      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6413      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6414      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6415      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6416      * by the specified number of milliseconds. If the event fires again within that time, the original
6417      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6418      * </ul><br>
6419      * <p>
6420      * <b>Combining Options</b><br>
6421      * Using the options argument, it is possible to combine different types of listeners:<br>
6422      * <br>
6423      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6424      * Code:<pre><code>
6425 el.on('click', this.onClick, this, {
6426     single: true,
6427     delay: 100,
6428     stopEvent : true,
6429     forumId: 4
6430 });</code></pre>
6431      * <p>
6432      * <b>Attaching multiple handlers in 1 call</b><br>
6433       * The method also allows for a single argument to be passed which is a config object containing properties
6434      * which specify multiple handlers.
6435      * <p>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : {
6439         fn: this.onClick
6440         scope: this,
6441         delay: 100
6442     },
6443     'mouseover' : {
6444         fn: this.onMouseOver
6445         scope: this
6446     },
6447     'mouseout' : {
6448         fn: this.onMouseOut
6449         scope: this
6450     }
6451 });</code></pre>
6452      * <p>
6453      * Or a shorthand syntax:<br>
6454      * Code:<pre><code>
6455 el.on({
6456     'click' : this.onClick,
6457     'mouseover' : this.onMouseOver,
6458     'mouseout' : this.onMouseOut
6459     scope: this
6460 });</code></pre>
6461      */
6462     pub.on = pub.addListener;
6463     pub.un = pub.removeListener;
6464
6465     pub.stoppedMouseDownEvent = new Roo.util.Event();
6466     return pub;
6467 }();
6468 /**
6469   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6470   * @param {Function} fn        The method the event invokes
6471   * @param {Object}   scope    An  object that becomes the scope of the handler
6472   * @param {boolean}  override If true, the obj passed in becomes
6473   *                             the execution scope of the listener
6474   * @member Roo
6475   * @method onReady
6476  */
6477 Roo.onReady = Roo.EventManager.onDocumentReady;
6478
6479 Roo.onReady(function(){
6480     var bd = Roo.get(document.body);
6481     if(!bd){ return; }
6482
6483     var cls = [
6484             Roo.isIE ? "roo-ie"
6485             : Roo.isGecko ? "roo-gecko"
6486             : Roo.isOpera ? "roo-opera"
6487             : Roo.isSafari ? "roo-safari" : ""];
6488
6489     if(Roo.isMac){
6490         cls.push("roo-mac");
6491     }
6492     if(Roo.isLinux){
6493         cls.push("roo-linux");
6494     }
6495     if(Roo.isBorderBox){
6496         cls.push('roo-border-box');
6497     }
6498     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6499         var p = bd.dom.parentNode;
6500         if(p){
6501             p.className += ' roo-strict';
6502         }
6503     }
6504     bd.addClass(cls.join(' '));
6505 });
6506
6507 /**
6508  * @class Roo.EventObject
6509  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6510  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6511  * Example:
6512  * <pre><code>
6513  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6514     e.preventDefault();
6515     var target = e.getTarget();
6516     ...
6517  }
6518  var myDiv = Roo.get("myDiv");
6519  myDiv.on("click", handleClick);
6520  //or
6521  Roo.EventManager.on("myDiv", 'click', handleClick);
6522  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6523  </code></pre>
6524  * @singleton
6525  */
6526 Roo.EventObject = function(){
6527     
6528     var E = Roo.lib.Event;
6529     
6530     // safari keypress events for special keys return bad keycodes
6531     var safariKeys = {
6532         63234 : 37, // left
6533         63235 : 39, // right
6534         63232 : 38, // up
6535         63233 : 40, // down
6536         63276 : 33, // page up
6537         63277 : 34, // page down
6538         63272 : 46, // delete
6539         63273 : 36, // home
6540         63275 : 35  // end
6541     };
6542
6543     // normalize button clicks
6544     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6545                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6546
6547     Roo.EventObjectImpl = function(e){
6548         if(e){
6549             this.setEvent(e.browserEvent || e);
6550         }
6551     };
6552     Roo.EventObjectImpl.prototype = {
6553         /**
6554          * Used to fix doc tools.
6555          * @scope Roo.EventObject.prototype
6556          */
6557             
6558
6559         
6560         
6561         /** The normal browser event */
6562         browserEvent : null,
6563         /** The button pressed in a mouse event */
6564         button : -1,
6565         /** True if the shift key was down during the event */
6566         shiftKey : false,
6567         /** True if the control key was down during the event */
6568         ctrlKey : false,
6569         /** True if the alt key was down during the event */
6570         altKey : false,
6571
6572         /** Key constant 
6573         * @type Number */
6574         BACKSPACE : 8,
6575         /** Key constant 
6576         * @type Number */
6577         TAB : 9,
6578         /** Key constant 
6579         * @type Number */
6580         RETURN : 13,
6581         /** Key constant 
6582         * @type Number */
6583         ENTER : 13,
6584         /** Key constant 
6585         * @type Number */
6586         SHIFT : 16,
6587         /** Key constant 
6588         * @type Number */
6589         CONTROL : 17,
6590         /** Key constant 
6591         * @type Number */
6592         ESC : 27,
6593         /** Key constant 
6594         * @type Number */
6595         SPACE : 32,
6596         /** Key constant 
6597         * @type Number */
6598         PAGEUP : 33,
6599         /** Key constant 
6600         * @type Number */
6601         PAGEDOWN : 34,
6602         /** Key constant 
6603         * @type Number */
6604         END : 35,
6605         /** Key constant 
6606         * @type Number */
6607         HOME : 36,
6608         /** Key constant 
6609         * @type Number */
6610         LEFT : 37,
6611         /** Key constant 
6612         * @type Number */
6613         UP : 38,
6614         /** Key constant 
6615         * @type Number */
6616         RIGHT : 39,
6617         /** Key constant 
6618         * @type Number */
6619         DOWN : 40,
6620         /** Key constant 
6621         * @type Number */
6622         DELETE : 46,
6623         /** Key constant 
6624         * @type Number */
6625         F5 : 116,
6626
6627            /** @private */
6628         setEvent : function(e){
6629             if(e == this || (e && e.browserEvent)){ // already wrapped
6630                 return e;
6631             }
6632             this.browserEvent = e;
6633             if(e){
6634                 // normalize buttons
6635                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6636                 if(e.type == 'click' && this.button == -1){
6637                     this.button = 0;
6638                 }
6639                 this.type = e.type;
6640                 this.shiftKey = e.shiftKey;
6641                 // mac metaKey behaves like ctrlKey
6642                 this.ctrlKey = e.ctrlKey || e.metaKey;
6643                 this.altKey = e.altKey;
6644                 // in getKey these will be normalized for the mac
6645                 this.keyCode = e.keyCode;
6646                 // keyup warnings on firefox.
6647                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6648                 // cache the target for the delayed and or buffered events
6649                 this.target = E.getTarget(e);
6650                 // same for XY
6651                 this.xy = E.getXY(e);
6652             }else{
6653                 this.button = -1;
6654                 this.shiftKey = false;
6655                 this.ctrlKey = false;
6656                 this.altKey = false;
6657                 this.keyCode = 0;
6658                 this.charCode =0;
6659                 this.target = null;
6660                 this.xy = [0, 0];
6661             }
6662             return this;
6663         },
6664
6665         /**
6666          * Stop the event (preventDefault and stopPropagation)
6667          */
6668         stopEvent : function(){
6669             if(this.browserEvent){
6670                 if(this.browserEvent.type == 'mousedown'){
6671                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6672                 }
6673                 E.stopEvent(this.browserEvent);
6674             }
6675         },
6676
6677         /**
6678          * Prevents the browsers default handling of the event.
6679          */
6680         preventDefault : function(){
6681             if(this.browserEvent){
6682                 E.preventDefault(this.browserEvent);
6683             }
6684         },
6685
6686         /** @private */
6687         isNavKeyPress : function(){
6688             var k = this.keyCode;
6689             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6690             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6691         },
6692
6693         isSpecialKey : function(){
6694             var k = this.keyCode;
6695             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6696             (k == 16) || (k == 17) ||
6697             (k >= 18 && k <= 20) ||
6698             (k >= 33 && k <= 35) ||
6699             (k >= 36 && k <= 39) ||
6700             (k >= 44 && k <= 45);
6701         },
6702         /**
6703          * Cancels bubbling of the event.
6704          */
6705         stopPropagation : function(){
6706             if(this.browserEvent){
6707                 if(this.type == 'mousedown'){
6708                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6709                 }
6710                 E.stopPropagation(this.browserEvent);
6711             }
6712         },
6713
6714         /**
6715          * Gets the key code for the event.
6716          * @return {Number}
6717          */
6718         getCharCode : function(){
6719             return this.charCode || this.keyCode;
6720         },
6721
6722         /**
6723          * Returns a normalized keyCode for the event.
6724          * @return {Number} The key code
6725          */
6726         getKey : function(){
6727             var k = this.keyCode || this.charCode;
6728             return Roo.isSafari ? (safariKeys[k] || k) : k;
6729         },
6730
6731         /**
6732          * Gets the x coordinate of the event.
6733          * @return {Number}
6734          */
6735         getPageX : function(){
6736             return this.xy[0];
6737         },
6738
6739         /**
6740          * Gets the y coordinate of the event.
6741          * @return {Number}
6742          */
6743         getPageY : function(){
6744             return this.xy[1];
6745         },
6746
6747         /**
6748          * Gets the time of the event.
6749          * @return {Number}
6750          */
6751         getTime : function(){
6752             if(this.browserEvent){
6753                 return E.getTime(this.browserEvent);
6754             }
6755             return null;
6756         },
6757
6758         /**
6759          * Gets the page coordinates of the event.
6760          * @return {Array} The xy values like [x, y]
6761          */
6762         getXY : function(){
6763             return this.xy;
6764         },
6765
6766         /**
6767          * Gets the target for the event.
6768          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6769          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6770                 search as a number or element (defaults to 10 || document.body)
6771          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6772          * @return {HTMLelement}
6773          */
6774         getTarget : function(selector, maxDepth, returnEl){
6775             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6776         },
6777         /**
6778          * Gets the related target.
6779          * @return {HTMLElement}
6780          */
6781         getRelatedTarget : function(){
6782             if(this.browserEvent){
6783                 return E.getRelatedTarget(this.browserEvent);
6784             }
6785             return null;
6786         },
6787
6788         /**
6789          * Normalizes mouse wheel delta across browsers
6790          * @return {Number} The delta
6791          */
6792         getWheelDelta : function(){
6793             var e = this.browserEvent;
6794             var delta = 0;
6795             if(e.wheelDelta){ /* IE/Opera. */
6796                 delta = e.wheelDelta/120;
6797             }else if(e.detail){ /* Mozilla case. */
6798                 delta = -e.detail/3;
6799             }
6800             return delta;
6801         },
6802
6803         /**
6804          * Returns true if the control, meta, shift or alt key was pressed during this event.
6805          * @return {Boolean}
6806          */
6807         hasModifier : function(){
6808             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6809         },
6810
6811         /**
6812          * Returns true if the target of this event equals el or is a child of el
6813          * @param {String/HTMLElement/Element} el
6814          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6815          * @return {Boolean}
6816          */
6817         within : function(el, related){
6818             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6819             return t && Roo.fly(el).contains(t);
6820         },
6821
6822         getPoint : function(){
6823             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6824         }
6825     };
6826
6827     return new Roo.EventObjectImpl();
6828 }();
6829             
6830     /*
6831  * Based on:
6832  * Ext JS Library 1.1.1
6833  * Copyright(c) 2006-2007, Ext JS, LLC.
6834  *
6835  * Originally Released Under LGPL - original licence link has changed is not relivant.
6836  *
6837  * Fork - LGPL
6838  * <script type="text/javascript">
6839  */
6840
6841  
6842 // was in Composite Element!??!?!
6843  
6844 (function(){
6845     var D = Roo.lib.Dom;
6846     var E = Roo.lib.Event;
6847     var A = Roo.lib.Anim;
6848
6849     // local style camelizing for speed
6850     var propCache = {};
6851     var camelRe = /(-[a-z])/gi;
6852     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6853     var view = document.defaultView;
6854
6855 /**
6856  * @class Roo.Element
6857  * Represents an Element in the DOM.<br><br>
6858  * Usage:<br>
6859 <pre><code>
6860 var el = Roo.get("my-div");
6861
6862 // or with getEl
6863 var el = getEl("my-div");
6864
6865 // or with a DOM element
6866 var el = Roo.get(myDivElement);
6867 </code></pre>
6868  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6869  * each call instead of constructing a new one.<br><br>
6870  * <b>Animations</b><br />
6871  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6872  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6873 <pre>
6874 Option    Default   Description
6875 --------- --------  ---------------------------------------------
6876 duration  .35       The duration of the animation in seconds
6877 easing    easeOut   The YUI easing method
6878 callback  none      A function to execute when the anim completes
6879 scope     this      The scope (this) of the callback function
6880 </pre>
6881 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6882 * manipulate the animation. Here's an example:
6883 <pre><code>
6884 var el = Roo.get("my-div");
6885
6886 // no animation
6887 el.setWidth(100);
6888
6889 // default animation
6890 el.setWidth(100, true);
6891
6892 // animation with some options set
6893 el.setWidth(100, {
6894     duration: 1,
6895     callback: this.foo,
6896     scope: this
6897 });
6898
6899 // using the "anim" property to get the Anim object
6900 var opt = {
6901     duration: 1,
6902     callback: this.foo,
6903     scope: this
6904 };
6905 el.setWidth(100, opt);
6906 ...
6907 if(opt.anim.isAnimated()){
6908     opt.anim.stop();
6909 }
6910 </code></pre>
6911 * <b> Composite (Collections of) Elements</b><br />
6912  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6913  * @constructor Create a new Element directly.
6914  * @param {String/HTMLElement} element
6915  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6916  */
6917     Roo.Element = function(element, forceNew){
6918         var dom = typeof element == "string" ?
6919                 document.getElementById(element) : element;
6920         if(!dom){ // invalid id/element
6921             return null;
6922         }
6923         var id = dom.id;
6924         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6925             return Roo.Element.cache[id];
6926         }
6927
6928         /**
6929          * The DOM element
6930          * @type HTMLElement
6931          */
6932         this.dom = dom;
6933
6934         /**
6935          * The DOM element ID
6936          * @type String
6937          */
6938         this.id = id || Roo.id(dom);
6939     };
6940
6941     var El = Roo.Element;
6942
6943     El.prototype = {
6944         /**
6945          * The element's default display mode  (defaults to "")
6946          * @type String
6947          */
6948         originalDisplay : "",
6949
6950         visibilityMode : 1,
6951         /**
6952          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6953          * @type String
6954          */
6955         defaultUnit : "px",
6956         /**
6957          * Sets the element's visibility mode. When setVisible() is called it
6958          * will use this to determine whether to set the visibility or the display property.
6959          * @param visMode Element.VISIBILITY or Element.DISPLAY
6960          * @return {Roo.Element} this
6961          */
6962         setVisibilityMode : function(visMode){
6963             this.visibilityMode = visMode;
6964             return this;
6965         },
6966         /**
6967          * Convenience method for setVisibilityMode(Element.DISPLAY)
6968          * @param {String} display (optional) What to set display to when visible
6969          * @return {Roo.Element} this
6970          */
6971         enableDisplayMode : function(display){
6972             this.setVisibilityMode(El.DISPLAY);
6973             if(typeof display != "undefined") this.originalDisplay = display;
6974             return this;
6975         },
6976
6977         /**
6978          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6979          * @param {String} selector The simple selector to test
6980          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6981                 search as a number or element (defaults to 10 || document.body)
6982          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6983          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6984          */
6985         findParent : function(simpleSelector, maxDepth, returnEl){
6986             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6987             maxDepth = maxDepth || 50;
6988             if(typeof maxDepth != "number"){
6989                 stopEl = Roo.getDom(maxDepth);
6990                 maxDepth = 10;
6991             }
6992             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6993                 if(dq.is(p, simpleSelector)){
6994                     return returnEl ? Roo.get(p) : p;
6995                 }
6996                 depth++;
6997                 p = p.parentNode;
6998             }
6999             return null;
7000         },
7001
7002
7003         /**
7004          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7005          * @param {String} selector The simple selector to test
7006          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7007                 search as a number or element (defaults to 10 || document.body)
7008          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7009          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7010          */
7011         findParentNode : function(simpleSelector, maxDepth, returnEl){
7012             var p = Roo.fly(this.dom.parentNode, '_internal');
7013             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7014         },
7015
7016         /**
7017          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7018          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7019          * @param {String} selector The simple selector to test
7020          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7021                 search as a number or element (defaults to 10 || document.body)
7022          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7023          */
7024         up : function(simpleSelector, maxDepth){
7025             return this.findParentNode(simpleSelector, maxDepth, true);
7026         },
7027
7028
7029
7030         /**
7031          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7032          * @param {String} selector The simple selector to test
7033          * @return {Boolean} True if this element matches the selector, else false
7034          */
7035         is : function(simpleSelector){
7036             return Roo.DomQuery.is(this.dom, simpleSelector);
7037         },
7038
7039         /**
7040          * Perform animation on this element.
7041          * @param {Object} args The YUI animation control args
7042          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7043          * @param {Function} onComplete (optional) Function to call when animation completes
7044          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7045          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7046          * @return {Roo.Element} this
7047          */
7048         animate : function(args, duration, onComplete, easing, animType){
7049             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7050             return this;
7051         },
7052
7053         /*
7054          * @private Internal animation call
7055          */
7056         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7057             animType = animType || 'run';
7058             opt = opt || {};
7059             var anim = Roo.lib.Anim[animType](
7060                 this.dom, args,
7061                 (opt.duration || defaultDur) || .35,
7062                 (opt.easing || defaultEase) || 'easeOut',
7063                 function(){
7064                     Roo.callback(cb, this);
7065                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7066                 },
7067                 this
7068             );
7069             opt.anim = anim;
7070             return anim;
7071         },
7072
7073         // private legacy anim prep
7074         preanim : function(a, i){
7075             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7076         },
7077
7078         /**
7079          * Removes worthless text nodes
7080          * @param {Boolean} forceReclean (optional) By default the element
7081          * keeps track if it has been cleaned already so
7082          * you can call this over and over. However, if you update the element and
7083          * need to force a reclean, you can pass true.
7084          */
7085         clean : function(forceReclean){
7086             if(this.isCleaned && forceReclean !== true){
7087                 return this;
7088             }
7089             var ns = /\S/;
7090             var d = this.dom, n = d.firstChild, ni = -1;
7091             while(n){
7092                 var nx = n.nextSibling;
7093                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7094                     d.removeChild(n);
7095                 }else{
7096                     n.nodeIndex = ++ni;
7097                 }
7098                 n = nx;
7099             }
7100             this.isCleaned = true;
7101             return this;
7102         },
7103
7104         // private
7105         calcOffsetsTo : function(el){
7106             el = Roo.get(el);
7107             var d = el.dom;
7108             var restorePos = false;
7109             if(el.getStyle('position') == 'static'){
7110                 el.position('relative');
7111                 restorePos = true;
7112             }
7113             var x = 0, y =0;
7114             var op = this.dom;
7115             while(op && op != d && op.tagName != 'HTML'){
7116                 x+= op.offsetLeft;
7117                 y+= op.offsetTop;
7118                 op = op.offsetParent;
7119             }
7120             if(restorePos){
7121                 el.position('static');
7122             }
7123             return [x, y];
7124         },
7125
7126         /**
7127          * Scrolls this element into view within the passed container.
7128          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7129          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7130          * @return {Roo.Element} this
7131          */
7132         scrollIntoView : function(container, hscroll){
7133             var c = Roo.getDom(container) || document.body;
7134             var el = this.dom;
7135
7136             var o = this.calcOffsetsTo(c),
7137                 l = o[0],
7138                 t = o[1],
7139                 b = t+el.offsetHeight,
7140                 r = l+el.offsetWidth;
7141
7142             var ch = c.clientHeight;
7143             var ct = parseInt(c.scrollTop, 10);
7144             var cl = parseInt(c.scrollLeft, 10);
7145             var cb = ct + ch;
7146             var cr = cl + c.clientWidth;
7147
7148             if(t < ct){
7149                 c.scrollTop = t;
7150             }else if(b > cb){
7151                 c.scrollTop = b-ch;
7152             }
7153
7154             if(hscroll !== false){
7155                 if(l < cl){
7156                     c.scrollLeft = l;
7157                 }else if(r > cr){
7158                     c.scrollLeft = r-c.clientWidth;
7159                 }
7160             }
7161             return this;
7162         },
7163
7164         // private
7165         scrollChildIntoView : function(child, hscroll){
7166             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7167         },
7168
7169         /**
7170          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7171          * the new height may not be available immediately.
7172          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7173          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7174          * @param {Function} onComplete (optional) Function to call when animation completes
7175          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7176          * @return {Roo.Element} this
7177          */
7178         autoHeight : function(animate, duration, onComplete, easing){
7179             var oldHeight = this.getHeight();
7180             this.clip();
7181             this.setHeight(1); // force clipping
7182             setTimeout(function(){
7183                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7184                 if(!animate){
7185                     this.setHeight(height);
7186                     this.unclip();
7187                     if(typeof onComplete == "function"){
7188                         onComplete();
7189                     }
7190                 }else{
7191                     this.setHeight(oldHeight); // restore original height
7192                     this.setHeight(height, animate, duration, function(){
7193                         this.unclip();
7194                         if(typeof onComplete == "function") onComplete();
7195                     }.createDelegate(this), easing);
7196                 }
7197             }.createDelegate(this), 0);
7198             return this;
7199         },
7200
7201         /**
7202          * Returns true if this element is an ancestor of the passed element
7203          * @param {HTMLElement/String} el The element to check
7204          * @return {Boolean} True if this element is an ancestor of el, else false
7205          */
7206         contains : function(el){
7207             if(!el){return false;}
7208             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7209         },
7210
7211         /**
7212          * Checks whether the element is currently visible using both visibility and display properties.
7213          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7214          * @return {Boolean} True if the element is currently visible, else false
7215          */
7216         isVisible : function(deep) {
7217             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7218             if(deep !== true || !vis){
7219                 return vis;
7220             }
7221             var p = this.dom.parentNode;
7222             while(p && p.tagName.toLowerCase() != "body"){
7223                 if(!Roo.fly(p, '_isVisible').isVisible()){
7224                     return false;
7225                 }
7226                 p = p.parentNode;
7227             }
7228             return true;
7229         },
7230
7231         /**
7232          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7233          * @param {String} selector The CSS selector
7234          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7235          * @return {CompositeElement/CompositeElementLite} The composite element
7236          */
7237         select : function(selector, unique){
7238             return El.select(selector, unique, this.dom);
7239         },
7240
7241         /**
7242          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7243          * @param {String} selector The CSS selector
7244          * @return {Array} An array of the matched nodes
7245          */
7246         query : function(selector, unique){
7247             return Roo.DomQuery.select(selector, this.dom);
7248         },
7249
7250         /**
7251          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7252          * @param {String} selector The CSS selector
7253          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7254          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7255          */
7256         child : function(selector, returnDom){
7257             var n = Roo.DomQuery.selectNode(selector, this.dom);
7258             return returnDom ? n : Roo.get(n);
7259         },
7260
7261         /**
7262          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7263          * @param {String} selector The CSS selector
7264          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7265          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7266          */
7267         down : function(selector, returnDom){
7268             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7269             return returnDom ? n : Roo.get(n);
7270         },
7271
7272         /**
7273          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7274          * @param {String} group The group the DD object is member of
7275          * @param {Object} config The DD config object
7276          * @param {Object} overrides An object containing methods to override/implement on the DD object
7277          * @return {Roo.dd.DD} The DD object
7278          */
7279         initDD : function(group, config, overrides){
7280             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7281             return Roo.apply(dd, overrides);
7282         },
7283
7284         /**
7285          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7286          * @param {String} group The group the DDProxy object is member of
7287          * @param {Object} config The DDProxy config object
7288          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7289          * @return {Roo.dd.DDProxy} The DDProxy object
7290          */
7291         initDDProxy : function(group, config, overrides){
7292             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7293             return Roo.apply(dd, overrides);
7294         },
7295
7296         /**
7297          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7298          * @param {String} group The group the DDTarget object is member of
7299          * @param {Object} config The DDTarget config object
7300          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7301          * @return {Roo.dd.DDTarget} The DDTarget object
7302          */
7303         initDDTarget : function(group, config, overrides){
7304             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7305             return Roo.apply(dd, overrides);
7306         },
7307
7308         /**
7309          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7310          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7311          * @param {Boolean} visible Whether the element is visible
7312          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7313          * @return {Roo.Element} this
7314          */
7315          setVisible : function(visible, animate){
7316             if(!animate || !A){
7317                 if(this.visibilityMode == El.DISPLAY){
7318                     this.setDisplayed(visible);
7319                 }else{
7320                     this.fixDisplay();
7321                     this.dom.style.visibility = visible ? "visible" : "hidden";
7322                 }
7323             }else{
7324                 // closure for composites
7325                 var dom = this.dom;
7326                 var visMode = this.visibilityMode;
7327                 if(visible){
7328                     this.setOpacity(.01);
7329                     this.setVisible(true);
7330                 }
7331                 this.anim({opacity: { to: (visible?1:0) }},
7332                       this.preanim(arguments, 1),
7333                       null, .35, 'easeIn', function(){
7334                          if(!visible){
7335                              if(visMode == El.DISPLAY){
7336                                  dom.style.display = "none";
7337                              }else{
7338                                  dom.style.visibility = "hidden";
7339                              }
7340                              Roo.get(dom).setOpacity(1);
7341                          }
7342                      });
7343             }
7344             return this;
7345         },
7346
7347         /**
7348          * Returns true if display is not "none"
7349          * @return {Boolean}
7350          */
7351         isDisplayed : function() {
7352             return this.getStyle("display") != "none";
7353         },
7354
7355         /**
7356          * Toggles the element's visibility or display, depending on visibility mode.
7357          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7358          * @return {Roo.Element} this
7359          */
7360         toggle : function(animate){
7361             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7362             return this;
7363         },
7364
7365         /**
7366          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7367          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7368          * @return {Roo.Element} this
7369          */
7370         setDisplayed : function(value) {
7371             if(typeof value == "boolean"){
7372                value = value ? this.originalDisplay : "none";
7373             }
7374             this.setStyle("display", value);
7375             return this;
7376         },
7377
7378         /**
7379          * Tries to focus the element. Any exceptions are caught and ignored.
7380          * @return {Roo.Element} this
7381          */
7382         focus : function() {
7383             try{
7384                 this.dom.focus();
7385             }catch(e){}
7386             return this;
7387         },
7388
7389         /**
7390          * Tries to blur the element. Any exceptions are caught and ignored.
7391          * @return {Roo.Element} this
7392          */
7393         blur : function() {
7394             try{
7395                 this.dom.blur();
7396             }catch(e){}
7397             return this;
7398         },
7399
7400         /**
7401          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7402          * @param {String/Array} className The CSS class to add, or an array of classes
7403          * @return {Roo.Element} this
7404          */
7405         addClass : function(className){
7406             if(className instanceof Array){
7407                 for(var i = 0, len = className.length; i < len; i++) {
7408                     this.addClass(className[i]);
7409                 }
7410             }else{
7411                 if(className && !this.hasClass(className)){
7412                     this.dom.className = this.dom.className + " " + className;
7413                 }
7414             }
7415             return this;
7416         },
7417
7418         /**
7419          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7420          * @param {String/Array} className The CSS class to add, or an array of classes
7421          * @return {Roo.Element} this
7422          */
7423         radioClass : function(className){
7424             var siblings = this.dom.parentNode.childNodes;
7425             for(var i = 0; i < siblings.length; i++) {
7426                 var s = siblings[i];
7427                 if(s.nodeType == 1){
7428                     Roo.get(s).removeClass(className);
7429                 }
7430             }
7431             this.addClass(className);
7432             return this;
7433         },
7434
7435         /**
7436          * Removes one or more CSS classes from the element.
7437          * @param {String/Array} className The CSS class to remove, or an array of classes
7438          * @return {Roo.Element} this
7439          */
7440         removeClass : function(className){
7441             if(!className || !this.dom.className){
7442                 return this;
7443             }
7444             if(className instanceof Array){
7445                 for(var i = 0, len = className.length; i < len; i++) {
7446                     this.removeClass(className[i]);
7447                 }
7448             }else{
7449                 if(this.hasClass(className)){
7450                     var re = this.classReCache[className];
7451                     if (!re) {
7452                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7453                        this.classReCache[className] = re;
7454                     }
7455                     this.dom.className =
7456                         this.dom.className.replace(re, " ");
7457                 }
7458             }
7459             return this;
7460         },
7461
7462         // private
7463         classReCache: {},
7464
7465         /**
7466          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7467          * @param {String} className The CSS class to toggle
7468          * @return {Roo.Element} this
7469          */
7470         toggleClass : function(className){
7471             if(this.hasClass(className)){
7472                 this.removeClass(className);
7473             }else{
7474                 this.addClass(className);
7475             }
7476             return this;
7477         },
7478
7479         /**
7480          * Checks if the specified CSS class exists on this element's DOM node.
7481          * @param {String} className The CSS class to check for
7482          * @return {Boolean} True if the class exists, else false
7483          */
7484         hasClass : function(className){
7485             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7486         },
7487
7488         /**
7489          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7490          * @param {String} oldClassName The CSS class to replace
7491          * @param {String} newClassName The replacement CSS class
7492          * @return {Roo.Element} this
7493          */
7494         replaceClass : function(oldClassName, newClassName){
7495             this.removeClass(oldClassName);
7496             this.addClass(newClassName);
7497             return this;
7498         },
7499
7500         /**
7501          * Returns an object with properties matching the styles requested.
7502          * For example, el.getStyles('color', 'font-size', 'width') might return
7503          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7504          * @param {String} style1 A style name
7505          * @param {String} style2 A style name
7506          * @param {String} etc.
7507          * @return {Object} The style object
7508          */
7509         getStyles : function(){
7510             var a = arguments, len = a.length, r = {};
7511             for(var i = 0; i < len; i++){
7512                 r[a[i]] = this.getStyle(a[i]);
7513             }
7514             return r;
7515         },
7516
7517         /**
7518          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7519          * @param {String} property The style property whose value is returned.
7520          * @return {String} The current value of the style property for this element.
7521          */
7522         getStyle : function(){
7523             return view && view.getComputedStyle ?
7524                 function(prop){
7525                     var el = this.dom, v, cs, camel;
7526                     if(prop == 'float'){
7527                         prop = "cssFloat";
7528                     }
7529                     if(el.style && (v = el.style[prop])){
7530                         return v;
7531                     }
7532                     if(cs = view.getComputedStyle(el, "")){
7533                         if(!(camel = propCache[prop])){
7534                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7535                         }
7536                         return cs[camel];
7537                     }
7538                     return null;
7539                 } :
7540                 function(prop){
7541                     var el = this.dom, v, cs, camel;
7542                     if(prop == 'opacity'){
7543                         if(typeof el.style.filter == 'string'){
7544                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7545                             if(m){
7546                                 var fv = parseFloat(m[1]);
7547                                 if(!isNaN(fv)){
7548                                     return fv ? fv / 100 : 0;
7549                                 }
7550                             }
7551                         }
7552                         return 1;
7553                     }else if(prop == 'float'){
7554                         prop = "styleFloat";
7555                     }
7556                     if(!(camel = propCache[prop])){
7557                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7558                     }
7559                     if(v = el.style[camel]){
7560                         return v;
7561                     }
7562                     if(cs = el.currentStyle){
7563                         return cs[camel];
7564                     }
7565                     return null;
7566                 };
7567         }(),
7568
7569         /**
7570          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7571          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7572          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7573          * @return {Roo.Element} this
7574          */
7575         setStyle : function(prop, value){
7576             if(typeof prop == "string"){
7577                 
7578                 if (prop == 'float') {
7579                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7580                     return this;
7581                 }
7582                 
7583                 var camel;
7584                 if(!(camel = propCache[prop])){
7585                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                 }
7587                 
7588                 if(camel == 'opacity') {
7589                     this.setOpacity(value);
7590                 }else{
7591                     this.dom.style[camel] = value;
7592                 }
7593             }else{
7594                 for(var style in prop){
7595                     if(typeof prop[style] != "function"){
7596                        this.setStyle(style, prop[style]);
7597                     }
7598                 }
7599             }
7600             return this;
7601         },
7602
7603         /**
7604          * More flexible version of {@link #setStyle} for setting style properties.
7605          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7606          * a function which returns such a specification.
7607          * @return {Roo.Element} this
7608          */
7609         applyStyles : function(style){
7610             Roo.DomHelper.applyStyles(this.dom, style);
7611             return this;
7612         },
7613
7614         /**
7615           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7616           * @return {Number} The X position of the element
7617           */
7618         getX : function(){
7619             return D.getX(this.dom);
7620         },
7621
7622         /**
7623           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7624           * @return {Number} The Y position of the element
7625           */
7626         getY : function(){
7627             return D.getY(this.dom);
7628         },
7629
7630         /**
7631           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7632           * @return {Array} The XY position of the element
7633           */
7634         getXY : function(){
7635             return D.getXY(this.dom);
7636         },
7637
7638         /**
7639          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7640          * @param {Number} The X position of the element
7641          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7642          * @return {Roo.Element} this
7643          */
7644         setX : function(x, animate){
7645             if(!animate || !A){
7646                 D.setX(this.dom, x);
7647             }else{
7648                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7649             }
7650             return this;
7651         },
7652
7653         /**
7654          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7655          * @param {Number} The Y position of the element
7656          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7657          * @return {Roo.Element} this
7658          */
7659         setY : function(y, animate){
7660             if(!animate || !A){
7661                 D.setY(this.dom, y);
7662             }else{
7663                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7664             }
7665             return this;
7666         },
7667
7668         /**
7669          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7670          * @param {String} left The left CSS property value
7671          * @return {Roo.Element} this
7672          */
7673         setLeft : function(left){
7674             this.setStyle("left", this.addUnits(left));
7675             return this;
7676         },
7677
7678         /**
7679          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7680          * @param {String} top The top CSS property value
7681          * @return {Roo.Element} this
7682          */
7683         setTop : function(top){
7684             this.setStyle("top", this.addUnits(top));
7685             return this;
7686         },
7687
7688         /**
7689          * Sets the element's CSS right style.
7690          * @param {String} right The right CSS property value
7691          * @return {Roo.Element} this
7692          */
7693         setRight : function(right){
7694             this.setStyle("right", this.addUnits(right));
7695             return this;
7696         },
7697
7698         /**
7699          * Sets the element's CSS bottom style.
7700          * @param {String} bottom The bottom CSS property value
7701          * @return {Roo.Element} this
7702          */
7703         setBottom : function(bottom){
7704             this.setStyle("bottom", this.addUnits(bottom));
7705             return this;
7706         },
7707
7708         /**
7709          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7710          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7711          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7712          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7713          * @return {Roo.Element} this
7714          */
7715         setXY : function(pos, animate){
7716             if(!animate || !A){
7717                 D.setXY(this.dom, pos);
7718             }else{
7719                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7720             }
7721             return this;
7722         },
7723
7724         /**
7725          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7726          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7727          * @param {Number} x X value for new position (coordinates are page-based)
7728          * @param {Number} y Y value for new position (coordinates are page-based)
7729          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7730          * @return {Roo.Element} this
7731          */
7732         setLocation : function(x, y, animate){
7733             this.setXY([x, y], this.preanim(arguments, 2));
7734             return this;
7735         },
7736
7737         /**
7738          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7739          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7740          * @param {Number} x X value for new position (coordinates are page-based)
7741          * @param {Number} y Y value for new position (coordinates are page-based)
7742          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7743          * @return {Roo.Element} this
7744          */
7745         moveTo : function(x, y, animate){
7746             this.setXY([x, y], this.preanim(arguments, 2));
7747             return this;
7748         },
7749
7750         /**
7751          * Returns the region of the given element.
7752          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7753          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7754          */
7755         getRegion : function(){
7756             return D.getRegion(this.dom);
7757         },
7758
7759         /**
7760          * Returns the offset height of the element
7761          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7762          * @return {Number} The element's height
7763          */
7764         getHeight : function(contentHeight){
7765             var h = this.dom.offsetHeight || 0;
7766             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7767         },
7768
7769         /**
7770          * Returns the offset width of the element
7771          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7772          * @return {Number} The element's width
7773          */
7774         getWidth : function(contentWidth){
7775             var w = this.dom.offsetWidth || 0;
7776             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7777         },
7778
7779         /**
7780          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7781          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7782          * if a height has not been set using CSS.
7783          * @return {Number}
7784          */
7785         getComputedHeight : function(){
7786             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7787             if(!h){
7788                 h = parseInt(this.getStyle('height'), 10) || 0;
7789                 if(!this.isBorderBox()){
7790                     h += this.getFrameWidth('tb');
7791                 }
7792             }
7793             return h;
7794         },
7795
7796         /**
7797          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7798          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7799          * if a width has not been set using CSS.
7800          * @return {Number}
7801          */
7802         getComputedWidth : function(){
7803             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7804             if(!w){
7805                 w = parseInt(this.getStyle('width'), 10) || 0;
7806                 if(!this.isBorderBox()){
7807                     w += this.getFrameWidth('lr');
7808                 }
7809             }
7810             return w;
7811         },
7812
7813         /**
7814          * Returns the size of the element.
7815          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7816          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7817          */
7818         getSize : function(contentSize){
7819             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7820         },
7821
7822         /**
7823          * Returns the width and height of the viewport.
7824          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7825          */
7826         getViewSize : function(){
7827             var d = this.dom, doc = document, aw = 0, ah = 0;
7828             if(d == doc || d == doc.body){
7829                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7830             }else{
7831                 return {
7832                     width : d.clientWidth,
7833                     height: d.clientHeight
7834                 };
7835             }
7836         },
7837
7838         /**
7839          * Returns the value of the "value" attribute
7840          * @param {Boolean} asNumber true to parse the value as a number
7841          * @return {String/Number}
7842          */
7843         getValue : function(asNumber){
7844             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7845         },
7846
7847         // private
7848         adjustWidth : function(width){
7849             if(typeof width == "number"){
7850                 if(this.autoBoxAdjust && !this.isBorderBox()){
7851                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7852                 }
7853                 if(width < 0){
7854                     width = 0;
7855                 }
7856             }
7857             return width;
7858         },
7859
7860         // private
7861         adjustHeight : function(height){
7862             if(typeof height == "number"){
7863                if(this.autoBoxAdjust && !this.isBorderBox()){
7864                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7865                }
7866                if(height < 0){
7867                    height = 0;
7868                }
7869             }
7870             return height;
7871         },
7872
7873         /**
7874          * Set the width of the element
7875          * @param {Number} width The new width
7876          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7877          * @return {Roo.Element} this
7878          */
7879         setWidth : function(width, animate){
7880             width = this.adjustWidth(width);
7881             if(!animate || !A){
7882                 this.dom.style.width = this.addUnits(width);
7883             }else{
7884                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7885             }
7886             return this;
7887         },
7888
7889         /**
7890          * Set the height of the element
7891          * @param {Number} height The new height
7892          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7893          * @return {Roo.Element} this
7894          */
7895          setHeight : function(height, animate){
7896             height = this.adjustHeight(height);
7897             if(!animate || !A){
7898                 this.dom.style.height = this.addUnits(height);
7899             }else{
7900                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7901             }
7902             return this;
7903         },
7904
7905         /**
7906          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7907          * @param {Number} width The new width
7908          * @param {Number} height The new height
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912          setSize : function(width, height, animate){
7913             if(typeof width == "object"){ // in case of object from getSize()
7914                 height = width.height; width = width.width;
7915             }
7916             width = this.adjustWidth(width); height = this.adjustHeight(height);
7917             if(!animate || !A){
7918                 this.dom.style.width = this.addUnits(width);
7919                 this.dom.style.height = this.addUnits(height);
7920             }else{
7921                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7922             }
7923             return this;
7924         },
7925
7926         /**
7927          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7928          * @param {Number} x X value for new position (coordinates are page-based)
7929          * @param {Number} y Y value for new position (coordinates are page-based)
7930          * @param {Number} width The new width
7931          * @param {Number} height The new height
7932          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7933          * @return {Roo.Element} this
7934          */
7935         setBounds : function(x, y, width, height, animate){
7936             if(!animate || !A){
7937                 this.setSize(width, height);
7938                 this.setLocation(x, y);
7939             }else{
7940                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7941                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7942                               this.preanim(arguments, 4), 'motion');
7943             }
7944             return this;
7945         },
7946
7947         /**
7948          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7949          * @param {Roo.lib.Region} region The region to fill
7950          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7951          * @return {Roo.Element} this
7952          */
7953         setRegion : function(region, animate){
7954             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7955             return this;
7956         },
7957
7958         /**
7959          * Appends an event handler
7960          *
7961          * @param {String}   eventName     The type of event to append
7962          * @param {Function} fn        The method the event invokes
7963          * @param {Object} scope       (optional) The scope (this object) of the fn
7964          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7965          */
7966         addListener : function(eventName, fn, scope, options){
7967             if (this.dom) {
7968                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7969             }
7970         },
7971
7972         /**
7973          * Removes an event handler from this element
7974          * @param {String} eventName the type of event to remove
7975          * @param {Function} fn the method the event invokes
7976          * @return {Roo.Element} this
7977          */
7978         removeListener : function(eventName, fn){
7979             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7980             return this;
7981         },
7982
7983         /**
7984          * Removes all previous added listeners from this element
7985          * @return {Roo.Element} this
7986          */
7987         removeAllListeners : function(){
7988             E.purgeElement(this.dom);
7989             return this;
7990         },
7991
7992         relayEvent : function(eventName, observable){
7993             this.on(eventName, function(e){
7994                 observable.fireEvent(eventName, e);
7995             });
7996         },
7997
7998         /**
7999          * Set the opacity of the element
8000          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8001          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8002          * @return {Roo.Element} this
8003          */
8004          setOpacity : function(opacity, animate){
8005             if(!animate || !A){
8006                 var s = this.dom.style;
8007                 if(Roo.isIE){
8008                     s.zoom = 1;
8009                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8010                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8011                 }else{
8012                     s.opacity = opacity;
8013                 }
8014             }else{
8015                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8016             }
8017             return this;
8018         },
8019
8020         /**
8021          * Gets the left X coordinate
8022          * @param {Boolean} local True to get the local css position instead of page coordinate
8023          * @return {Number}
8024          */
8025         getLeft : function(local){
8026             if(!local){
8027                 return this.getX();
8028             }else{
8029                 return parseInt(this.getStyle("left"), 10) || 0;
8030             }
8031         },
8032
8033         /**
8034          * Gets the right X coordinate of the element (element X position + element width)
8035          * @param {Boolean} local True to get the local css position instead of page coordinate
8036          * @return {Number}
8037          */
8038         getRight : function(local){
8039             if(!local){
8040                 return this.getX() + this.getWidth();
8041             }else{
8042                 return (this.getLeft(true) + this.getWidth()) || 0;
8043             }
8044         },
8045
8046         /**
8047          * Gets the top Y coordinate
8048          * @param {Boolean} local True to get the local css position instead of page coordinate
8049          * @return {Number}
8050          */
8051         getTop : function(local) {
8052             if(!local){
8053                 return this.getY();
8054             }else{
8055                 return parseInt(this.getStyle("top"), 10) || 0;
8056             }
8057         },
8058
8059         /**
8060          * Gets the bottom Y coordinate of the element (element Y position + element height)
8061          * @param {Boolean} local True to get the local css position instead of page coordinate
8062          * @return {Number}
8063          */
8064         getBottom : function(local){
8065             if(!local){
8066                 return this.getY() + this.getHeight();
8067             }else{
8068                 return (this.getTop(true) + this.getHeight()) || 0;
8069             }
8070         },
8071
8072         /**
8073         * Initializes positioning on this element. If a desired position is not passed, it will make the
8074         * the element positioned relative IF it is not already positioned.
8075         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8076         * @param {Number} zIndex (optional) The zIndex to apply
8077         * @param {Number} x (optional) Set the page X position
8078         * @param {Number} y (optional) Set the page Y position
8079         */
8080         position : function(pos, zIndex, x, y){
8081             if(!pos){
8082                if(this.getStyle('position') == 'static'){
8083                    this.setStyle('position', 'relative');
8084                }
8085             }else{
8086                 this.setStyle("position", pos);
8087             }
8088             if(zIndex){
8089                 this.setStyle("z-index", zIndex);
8090             }
8091             if(x !== undefined && y !== undefined){
8092                 this.setXY([x, y]);
8093             }else if(x !== undefined){
8094                 this.setX(x);
8095             }else if(y !== undefined){
8096                 this.setY(y);
8097             }
8098         },
8099
8100         /**
8101         * Clear positioning back to the default when the document was loaded
8102         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8103         * @return {Roo.Element} this
8104          */
8105         clearPositioning : function(value){
8106             value = value ||'';
8107             this.setStyle({
8108                 "left": value,
8109                 "right": value,
8110                 "top": value,
8111                 "bottom": value,
8112                 "z-index": "",
8113                 "position" : "static"
8114             });
8115             return this;
8116         },
8117
8118         /**
8119         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8120         * snapshot before performing an update and then restoring the element.
8121         * @return {Object}
8122         */
8123         getPositioning : function(){
8124             var l = this.getStyle("left");
8125             var t = this.getStyle("top");
8126             return {
8127                 "position" : this.getStyle("position"),
8128                 "left" : l,
8129                 "right" : l ? "" : this.getStyle("right"),
8130                 "top" : t,
8131                 "bottom" : t ? "" : this.getStyle("bottom"),
8132                 "z-index" : this.getStyle("z-index")
8133             };
8134         },
8135
8136         /**
8137          * Gets the width of the border(s) for the specified side(s)
8138          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8139          * passing lr would get the border (l)eft width + the border (r)ight width.
8140          * @return {Number} The width of the sides passed added together
8141          */
8142         getBorderWidth : function(side){
8143             return this.addStyles(side, El.borders);
8144         },
8145
8146         /**
8147          * Gets the width of the padding(s) for the specified side(s)
8148          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8149          * passing lr would get the padding (l)eft + the padding (r)ight.
8150          * @return {Number} The padding of the sides passed added together
8151          */
8152         getPadding : function(side){
8153             return this.addStyles(side, El.paddings);
8154         },
8155
8156         /**
8157         * Set positioning with an object returned by getPositioning().
8158         * @param {Object} posCfg
8159         * @return {Roo.Element} this
8160          */
8161         setPositioning : function(pc){
8162             this.applyStyles(pc);
8163             if(pc.right == "auto"){
8164                 this.dom.style.right = "";
8165             }
8166             if(pc.bottom == "auto"){
8167                 this.dom.style.bottom = "";
8168             }
8169             return this;
8170         },
8171
8172         // private
8173         fixDisplay : function(){
8174             if(this.getStyle("display") == "none"){
8175                 this.setStyle("visibility", "hidden");
8176                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8177                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8178                     this.setStyle("display", "block");
8179                 }
8180             }
8181         },
8182
8183         /**
8184          * Quick set left and top adding default units
8185          * @param {String} left The left CSS property value
8186          * @param {String} top The top CSS property value
8187          * @return {Roo.Element} this
8188          */
8189          setLeftTop : function(left, top){
8190             this.dom.style.left = this.addUnits(left);
8191             this.dom.style.top = this.addUnits(top);
8192             return this;
8193         },
8194
8195         /**
8196          * Move this element relative to its current position.
8197          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8198          * @param {Number} distance How far to move the element in pixels
8199          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8200          * @return {Roo.Element} this
8201          */
8202          move : function(direction, distance, animate){
8203             var xy = this.getXY();
8204             direction = direction.toLowerCase();
8205             switch(direction){
8206                 case "l":
8207                 case "left":
8208                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8209                     break;
8210                case "r":
8211                case "right":
8212                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8213                     break;
8214                case "t":
8215                case "top":
8216                case "up":
8217                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8218                     break;
8219                case "b":
8220                case "bottom":
8221                case "down":
8222                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8223                     break;
8224             }
8225             return this;
8226         },
8227
8228         /**
8229          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8230          * @return {Roo.Element} this
8231          */
8232         clip : function(){
8233             if(!this.isClipped){
8234                this.isClipped = true;
8235                this.originalClip = {
8236                    "o": this.getStyle("overflow"),
8237                    "x": this.getStyle("overflow-x"),
8238                    "y": this.getStyle("overflow-y")
8239                };
8240                this.setStyle("overflow", "hidden");
8241                this.setStyle("overflow-x", "hidden");
8242                this.setStyle("overflow-y", "hidden");
8243             }
8244             return this;
8245         },
8246
8247         /**
8248          *  Return clipping (overflow) to original clipping before clip() was called
8249          * @return {Roo.Element} this
8250          */
8251         unclip : function(){
8252             if(this.isClipped){
8253                 this.isClipped = false;
8254                 var o = this.originalClip;
8255                 if(o.o){this.setStyle("overflow", o.o);}
8256                 if(o.x){this.setStyle("overflow-x", o.x);}
8257                 if(o.y){this.setStyle("overflow-y", o.y);}
8258             }
8259             return this;
8260         },
8261
8262
8263         /**
8264          * Gets the x,y coordinates specified by the anchor position on the element.
8265          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8266          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8267          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8268          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8269          * @return {Array} [x, y] An array containing the element's x and y coordinates
8270          */
8271         getAnchorXY : function(anchor, local, s){
8272             //Passing a different size is useful for pre-calculating anchors,
8273             //especially for anchored animations that change the el size.
8274
8275             var w, h, vp = false;
8276             if(!s){
8277                 var d = this.dom;
8278                 if(d == document.body || d == document){
8279                     vp = true;
8280                     w = D.getViewWidth(); h = D.getViewHeight();
8281                 }else{
8282                     w = this.getWidth(); h = this.getHeight();
8283                 }
8284             }else{
8285                 w = s.width;  h = s.height;
8286             }
8287             var x = 0, y = 0, r = Math.round;
8288             switch((anchor || "tl").toLowerCase()){
8289                 case "c":
8290                     x = r(w*.5);
8291                     y = r(h*.5);
8292                 break;
8293                 case "t":
8294                     x = r(w*.5);
8295                     y = 0;
8296                 break;
8297                 case "l":
8298                     x = 0;
8299                     y = r(h*.5);
8300                 break;
8301                 case "r":
8302                     x = w;
8303                     y = r(h*.5);
8304                 break;
8305                 case "b":
8306                     x = r(w*.5);
8307                     y = h;
8308                 break;
8309                 case "tl":
8310                     x = 0;
8311                     y = 0;
8312                 break;
8313                 case "bl":
8314                     x = 0;
8315                     y = h;
8316                 break;
8317                 case "br":
8318                     x = w;
8319                     y = h;
8320                 break;
8321                 case "tr":
8322                     x = w;
8323                     y = 0;
8324                 break;
8325             }
8326             if(local === true){
8327                 return [x, y];
8328             }
8329             if(vp){
8330                 var sc = this.getScroll();
8331                 return [x + sc.left, y + sc.top];
8332             }
8333             //Add the element's offset xy
8334             var o = this.getXY();
8335             return [x+o[0], y+o[1]];
8336         },
8337
8338         /**
8339          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8340          * supported position values.
8341          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8342          * @param {String} position The position to align to.
8343          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8344          * @return {Array} [x, y]
8345          */
8346         getAlignToXY : function(el, p, o){
8347             el = Roo.get(el);
8348             var d = this.dom;
8349             if(!el.dom){
8350                 throw "Element.alignTo with an element that doesn't exist";
8351             }
8352             var c = false; //constrain to viewport
8353             var p1 = "", p2 = "";
8354             o = o || [0,0];
8355
8356             if(!p){
8357                 p = "tl-bl";
8358             }else if(p == "?"){
8359                 p = "tl-bl?";
8360             }else if(p.indexOf("-") == -1){
8361                 p = "tl-" + p;
8362             }
8363             p = p.toLowerCase();
8364             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8365             if(!m){
8366                throw "Element.alignTo with an invalid alignment " + p;
8367             }
8368             p1 = m[1]; p2 = m[2]; c = !!m[3];
8369
8370             //Subtract the aligned el's internal xy from the target's offset xy
8371             //plus custom offset to get the aligned el's new offset xy
8372             var a1 = this.getAnchorXY(p1, true);
8373             var a2 = el.getAnchorXY(p2, false);
8374             var x = a2[0] - a1[0] + o[0];
8375             var y = a2[1] - a1[1] + o[1];
8376             if(c){
8377                 //constrain the aligned el to viewport if necessary
8378                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8379                 // 5px of margin for ie
8380                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8381
8382                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8383                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8384                 //otherwise swap the aligned el to the opposite border of the target.
8385                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8386                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8387                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8388                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8389
8390                var doc = document;
8391                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8392                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8393
8394                if((x+w) > dw + scrollX){
8395                     x = swapX ? r.left-w : dw+scrollX-w;
8396                 }
8397                if(x < scrollX){
8398                    x = swapX ? r.right : scrollX;
8399                }
8400                if((y+h) > dh + scrollY){
8401                     y = swapY ? r.top-h : dh+scrollY-h;
8402                 }
8403                if (y < scrollY){
8404                    y = swapY ? r.bottom : scrollY;
8405                }
8406             }
8407             return [x,y];
8408         },
8409
8410         // private
8411         getConstrainToXY : function(){
8412             var os = {top:0, left:0, bottom:0, right: 0};
8413
8414             return function(el, local, offsets, proposedXY){
8415                 el = Roo.get(el);
8416                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8417
8418                 var vw, vh, vx = 0, vy = 0;
8419                 if(el.dom == document.body || el.dom == document){
8420                     vw = Roo.lib.Dom.getViewWidth();
8421                     vh = Roo.lib.Dom.getViewHeight();
8422                 }else{
8423                     vw = el.dom.clientWidth;
8424                     vh = el.dom.clientHeight;
8425                     if(!local){
8426                         var vxy = el.getXY();
8427                         vx = vxy[0];
8428                         vy = vxy[1];
8429                     }
8430                 }
8431
8432                 var s = el.getScroll();
8433
8434                 vx += offsets.left + s.left;
8435                 vy += offsets.top + s.top;
8436
8437                 vw -= offsets.right;
8438                 vh -= offsets.bottom;
8439
8440                 var vr = vx+vw;
8441                 var vb = vy+vh;
8442
8443                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8444                 var x = xy[0], y = xy[1];
8445                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8446
8447                 // only move it if it needs it
8448                 var moved = false;
8449
8450                 // first validate right/bottom
8451                 if((x + w) > vr){
8452                     x = vr - w;
8453                     moved = true;
8454                 }
8455                 if((y + h) > vb){
8456                     y = vb - h;
8457                     moved = true;
8458                 }
8459                 // then make sure top/left isn't negative
8460                 if(x < vx){
8461                     x = vx;
8462                     moved = true;
8463                 }
8464                 if(y < vy){
8465                     y = vy;
8466                     moved = true;
8467                 }
8468                 return moved ? [x, y] : false;
8469             };
8470         }(),
8471
8472         // private
8473         adjustForConstraints : function(xy, parent, offsets){
8474             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8475         },
8476
8477         /**
8478          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8479          * document it aligns it to the viewport.
8480          * The position parameter is optional, and can be specified in any one of the following formats:
8481          * <ul>
8482          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8483          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8484          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8485          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8486          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8487          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8488          * </ul>
8489          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8490          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8491          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8492          * that specified in order to enforce the viewport constraints.
8493          * Following are all of the supported anchor positions:
8494     <pre>
8495     Value  Description
8496     -----  -----------------------------
8497     tl     The top left corner (default)
8498     t      The center of the top edge
8499     tr     The top right corner
8500     l      The center of the left edge
8501     c      In the center of the element
8502     r      The center of the right edge
8503     bl     The bottom left corner
8504     b      The center of the bottom edge
8505     br     The bottom right corner
8506     </pre>
8507     Example Usage:
8508     <pre><code>
8509     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8510     el.alignTo("other-el");
8511
8512     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8513     el.alignTo("other-el", "tr?");
8514
8515     // align the bottom right corner of el with the center left edge of other-el
8516     el.alignTo("other-el", "br-l?");
8517
8518     // align the center of el with the bottom left corner of other-el and
8519     // adjust the x position by -6 pixels (and the y position by 0)
8520     el.alignTo("other-el", "c-bl", [-6, 0]);
8521     </code></pre>
8522          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8523          * @param {String} position The position to align to.
8524          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8525          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8526          * @return {Roo.Element} this
8527          */
8528         alignTo : function(element, position, offsets, animate){
8529             var xy = this.getAlignToXY(element, position, offsets);
8530             this.setXY(xy, this.preanim(arguments, 3));
8531             return this;
8532         },
8533
8534         /**
8535          * Anchors an element to another element and realigns it when the window is resized.
8536          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8537          * @param {String} position The position to align to.
8538          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8539          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8540          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8541          * is a number, it is used as the buffer delay (defaults to 50ms).
8542          * @param {Function} callback The function to call after the animation finishes
8543          * @return {Roo.Element} this
8544          */
8545         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8546             var action = function(){
8547                 this.alignTo(el, alignment, offsets, animate);
8548                 Roo.callback(callback, this);
8549             };
8550             Roo.EventManager.onWindowResize(action, this);
8551             var tm = typeof monitorScroll;
8552             if(tm != 'undefined'){
8553                 Roo.EventManager.on(window, 'scroll', action, this,
8554                     {buffer: tm == 'number' ? monitorScroll : 50});
8555             }
8556             action.call(this); // align immediately
8557             return this;
8558         },
8559         /**
8560          * Clears any opacity settings from this element. Required in some cases for IE.
8561          * @return {Roo.Element} this
8562          */
8563         clearOpacity : function(){
8564             if (window.ActiveXObject) {
8565                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8566                     this.dom.style.filter = "";
8567                 }
8568             } else {
8569                 this.dom.style.opacity = "";
8570                 this.dom.style["-moz-opacity"] = "";
8571                 this.dom.style["-khtml-opacity"] = "";
8572             }
8573             return this;
8574         },
8575
8576         /**
8577          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8578          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8579          * @return {Roo.Element} this
8580          */
8581         hide : function(animate){
8582             this.setVisible(false, this.preanim(arguments, 0));
8583             return this;
8584         },
8585
8586         /**
8587         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8588         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8589          * @return {Roo.Element} this
8590          */
8591         show : function(animate){
8592             this.setVisible(true, this.preanim(arguments, 0));
8593             return this;
8594         },
8595
8596         /**
8597          * @private Test if size has a unit, otherwise appends the default
8598          */
8599         addUnits : function(size){
8600             return Roo.Element.addUnits(size, this.defaultUnit);
8601         },
8602
8603         /**
8604          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8605          * @return {Roo.Element} this
8606          */
8607         beginMeasure : function(){
8608             var el = this.dom;
8609             if(el.offsetWidth || el.offsetHeight){
8610                 return this; // offsets work already
8611             }
8612             var changed = [];
8613             var p = this.dom, b = document.body; // start with this element
8614             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8615                 var pe = Roo.get(p);
8616                 if(pe.getStyle('display') == 'none'){
8617                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8618                     p.style.visibility = "hidden";
8619                     p.style.display = "block";
8620                 }
8621                 p = p.parentNode;
8622             }
8623             this._measureChanged = changed;
8624             return this;
8625
8626         },
8627
8628         /**
8629          * Restores displays to before beginMeasure was called
8630          * @return {Roo.Element} this
8631          */
8632         endMeasure : function(){
8633             var changed = this._measureChanged;
8634             if(changed){
8635                 for(var i = 0, len = changed.length; i < len; i++) {
8636                     var r = changed[i];
8637                     r.el.style.visibility = r.visibility;
8638                     r.el.style.display = "none";
8639                 }
8640                 this._measureChanged = null;
8641             }
8642             return this;
8643         },
8644
8645         /**
8646         * Update the innerHTML of this element, optionally searching for and processing scripts
8647         * @param {String} html The new HTML
8648         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8649         * @param {Function} callback For async script loading you can be noticed when the update completes
8650         * @return {Roo.Element} this
8651          */
8652         update : function(html, loadScripts, callback){
8653             if(typeof html == "undefined"){
8654                 html = "";
8655             }
8656             if(loadScripts !== true){
8657                 this.dom.innerHTML = html;
8658                 if(typeof callback == "function"){
8659                     callback();
8660                 }
8661                 return this;
8662             }
8663             var id = Roo.id();
8664             var dom = this.dom;
8665
8666             html += '<span id="' + id + '"></span>';
8667
8668             E.onAvailable(id, function(){
8669                 var hd = document.getElementsByTagName("head")[0];
8670                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8671                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8672                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8673
8674                 var match;
8675                 while(match = re.exec(html)){
8676                     var attrs = match[1];
8677                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8678                     if(srcMatch && srcMatch[2]){
8679                        var s = document.createElement("script");
8680                        s.src = srcMatch[2];
8681                        var typeMatch = attrs.match(typeRe);
8682                        if(typeMatch && typeMatch[2]){
8683                            s.type = typeMatch[2];
8684                        }
8685                        hd.appendChild(s);
8686                     }else if(match[2] && match[2].length > 0){
8687                         if(window.execScript) {
8688                            window.execScript(match[2]);
8689                         } else {
8690                             /**
8691                              * eval:var:id
8692                              * eval:var:dom
8693                              * eval:var:html
8694                              * 
8695                              */
8696                            window.eval(match[2]);
8697                         }
8698                     }
8699                 }
8700                 var el = document.getElementById(id);
8701                 if(el){el.parentNode.removeChild(el);}
8702                 if(typeof callback == "function"){
8703                     callback();
8704                 }
8705             });
8706             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8707             return this;
8708         },
8709
8710         /**
8711          * Direct access to the UpdateManager update() method (takes the same parameters).
8712          * @param {String/Function} url The url for this request or a function to call to get the url
8713          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8714          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8715          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8716          * @return {Roo.Element} this
8717          */
8718         load : function(){
8719             var um = this.getUpdateManager();
8720             um.update.apply(um, arguments);
8721             return this;
8722         },
8723
8724         /**
8725         * Gets this element's UpdateManager
8726         * @return {Roo.UpdateManager} The UpdateManager
8727         */
8728         getUpdateManager : function(){
8729             if(!this.updateManager){
8730                 this.updateManager = new Roo.UpdateManager(this);
8731             }
8732             return this.updateManager;
8733         },
8734
8735         /**
8736          * Disables text selection for this element (normalized across browsers)
8737          * @return {Roo.Element} this
8738          */
8739         unselectable : function(){
8740             this.dom.unselectable = "on";
8741             this.swallowEvent("selectstart", true);
8742             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8743             this.addClass("x-unselectable");
8744             return this;
8745         },
8746
8747         /**
8748         * Calculates the x, y to center this element on the screen
8749         * @return {Array} The x, y values [x, y]
8750         */
8751         getCenterXY : function(){
8752             return this.getAlignToXY(document, 'c-c');
8753         },
8754
8755         /**
8756         * Centers the Element in either the viewport, or another Element.
8757         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8758         */
8759         center : function(centerIn){
8760             this.alignTo(centerIn || document, 'c-c');
8761             return this;
8762         },
8763
8764         /**
8765          * Tests various css rules/browsers to determine if this element uses a border box
8766          * @return {Boolean}
8767          */
8768         isBorderBox : function(){
8769             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8770         },
8771
8772         /**
8773          * Return a box {x, y, width, height} that can be used to set another elements
8774          * size/location to match this element.
8775          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8776          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8777          * @return {Object} box An object in the format {x, y, width, height}
8778          */
8779         getBox : function(contentBox, local){
8780             var xy;
8781             if(!local){
8782                 xy = this.getXY();
8783             }else{
8784                 var left = parseInt(this.getStyle("left"), 10) || 0;
8785                 var top = parseInt(this.getStyle("top"), 10) || 0;
8786                 xy = [left, top];
8787             }
8788             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8789             if(!contentBox){
8790                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8791             }else{
8792                 var l = this.getBorderWidth("l")+this.getPadding("l");
8793                 var r = this.getBorderWidth("r")+this.getPadding("r");
8794                 var t = this.getBorderWidth("t")+this.getPadding("t");
8795                 var b = this.getBorderWidth("b")+this.getPadding("b");
8796                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8797             }
8798             bx.right = bx.x + bx.width;
8799             bx.bottom = bx.y + bx.height;
8800             return bx;
8801         },
8802
8803         /**
8804          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8805          for more information about the sides.
8806          * @param {String} sides
8807          * @return {Number}
8808          */
8809         getFrameWidth : function(sides, onlyContentBox){
8810             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8811         },
8812
8813         /**
8814          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8815          * @param {Object} box The box to fill {x, y, width, height}
8816          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8817          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8818          * @return {Roo.Element} this
8819          */
8820         setBox : function(box, adjust, animate){
8821             var w = box.width, h = box.height;
8822             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8823                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8824                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8825             }
8826             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8827             return this;
8828         },
8829
8830         /**
8831          * Forces the browser to repaint this element
8832          * @return {Roo.Element} this
8833          */
8834          repaint : function(){
8835             var dom = this.dom;
8836             this.addClass("x-repaint");
8837             setTimeout(function(){
8838                 Roo.get(dom).removeClass("x-repaint");
8839             }, 1);
8840             return this;
8841         },
8842
8843         /**
8844          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8845          * then it returns the calculated width of the sides (see getPadding)
8846          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8847          * @return {Object/Number}
8848          */
8849         getMargins : function(side){
8850             if(!side){
8851                 return {
8852                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8853                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8854                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8855                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8856                 };
8857             }else{
8858                 return this.addStyles(side, El.margins);
8859              }
8860         },
8861
8862         // private
8863         addStyles : function(sides, styles){
8864             var val = 0, v, w;
8865             for(var i = 0, len = sides.length; i < len; i++){
8866                 v = this.getStyle(styles[sides.charAt(i)]);
8867                 if(v){
8868                      w = parseInt(v, 10);
8869                      if(w){ val += w; }
8870                 }
8871             }
8872             return val;
8873         },
8874
8875         /**
8876          * Creates a proxy element of this element
8877          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8878          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8879          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8880          * @return {Roo.Element} The new proxy element
8881          */
8882         createProxy : function(config, renderTo, matchBox){
8883             if(renderTo){
8884                 renderTo = Roo.getDom(renderTo);
8885             }else{
8886                 renderTo = document.body;
8887             }
8888             config = typeof config == "object" ?
8889                 config : {tag : "div", cls: config};
8890             var proxy = Roo.DomHelper.append(renderTo, config, true);
8891             if(matchBox){
8892                proxy.setBox(this.getBox());
8893             }
8894             return proxy;
8895         },
8896
8897         /**
8898          * Puts a mask over this element to disable user interaction. Requires core.css.
8899          * This method can only be applied to elements which accept child nodes.
8900          * @param {String} msg (optional) A message to display in the mask
8901          * @param {String} msgCls (optional) A css class to apply to the msg element
8902          * @return {Element} The mask  element
8903          */
8904         mask : function(msg, msgCls)
8905         {
8906             if(this.getStyle("position") == "static"){
8907                 this.setStyle("position", "relative");
8908             }
8909             if(!this._mask){
8910                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8911             }
8912             this.addClass("x-masked");
8913             this._mask.setDisplayed(true);
8914             
8915             // we wander
8916             var z = 0;
8917             var dom = this.dom
8918             while (dom && dom.style) {
8919                 if (!isNaN(parseInt(dom.style.zIndex))) {
8920                     z = Math.max(z, parseInt(dom.style.zIndex));
8921                 }
8922                 dom = dom.parentNode;
8923             }
8924             // if we are masking the body - then it hides everything..
8925             if (this.dom == document.body) {
8926                 z = 1000000;
8927                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8928                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8929             }
8930            
8931             if(typeof msg == 'string'){
8932                 if(!this._maskMsg){
8933                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8934                 }
8935                 var mm = this._maskMsg;
8936                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8937                 mm.dom.firstChild.innerHTML = msg;
8938                 mm.setDisplayed(true);
8939                 mm.center(this);
8940                 mm.setStyle('z-index', z + 102);
8941             }
8942             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8943                 this._mask.setHeight(this.getHeight());
8944             }
8945             this._mask.setStyle('z-index', z + 100);
8946             
8947             return this._mask;
8948         },
8949
8950         /**
8951          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8952          * it is cached for reuse.
8953          */
8954         unmask : function(removeEl){
8955             if(this._mask){
8956                 if(removeEl === true){
8957                     this._mask.remove();
8958                     delete this._mask;
8959                     if(this._maskMsg){
8960                         this._maskMsg.remove();
8961                         delete this._maskMsg;
8962                     }
8963                 }else{
8964                     this._mask.setDisplayed(false);
8965                     if(this._maskMsg){
8966                         this._maskMsg.setDisplayed(false);
8967                     }
8968                 }
8969             }
8970             this.removeClass("x-masked");
8971         },
8972
8973         /**
8974          * Returns true if this element is masked
8975          * @return {Boolean}
8976          */
8977         isMasked : function(){
8978             return this._mask && this._mask.isVisible();
8979         },
8980
8981         /**
8982          * Creates an iframe shim for this element to keep selects and other windowed objects from
8983          * showing through.
8984          * @return {Roo.Element} The new shim element
8985          */
8986         createShim : function(){
8987             var el = document.createElement('iframe');
8988             el.frameBorder = 'no';
8989             el.className = 'roo-shim';
8990             if(Roo.isIE && Roo.isSecure){
8991                 el.src = Roo.SSL_SECURE_URL;
8992             }
8993             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8994             shim.autoBoxAdjust = false;
8995             return shim;
8996         },
8997
8998         /**
8999          * Removes this element from the DOM and deletes it from the cache
9000          */
9001         remove : function(){
9002             if(this.dom.parentNode){
9003                 this.dom.parentNode.removeChild(this.dom);
9004             }
9005             delete El.cache[this.dom.id];
9006         },
9007
9008         /**
9009          * Sets up event handlers to add and remove a css class when the mouse is over this element
9010          * @param {String} className
9011          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9012          * mouseout events for children elements
9013          * @return {Roo.Element} this
9014          */
9015         addClassOnOver : function(className, preventFlicker){
9016             this.on("mouseover", function(){
9017                 Roo.fly(this, '_internal').addClass(className);
9018             }, this.dom);
9019             var removeFn = function(e){
9020                 if(preventFlicker !== true || !e.within(this, true)){
9021                     Roo.fly(this, '_internal').removeClass(className);
9022                 }
9023             };
9024             this.on("mouseout", removeFn, this.dom);
9025             return this;
9026         },
9027
9028         /**
9029          * Sets up event handlers to add and remove a css class when this element has the focus
9030          * @param {String} className
9031          * @return {Roo.Element} this
9032          */
9033         addClassOnFocus : function(className){
9034             this.on("focus", function(){
9035                 Roo.fly(this, '_internal').addClass(className);
9036             }, this.dom);
9037             this.on("blur", function(){
9038                 Roo.fly(this, '_internal').removeClass(className);
9039             }, this.dom);
9040             return this;
9041         },
9042         /**
9043          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9044          * @param {String} className
9045          * @return {Roo.Element} this
9046          */
9047         addClassOnClick : function(className){
9048             var dom = this.dom;
9049             this.on("mousedown", function(){
9050                 Roo.fly(dom, '_internal').addClass(className);
9051                 var d = Roo.get(document);
9052                 var fn = function(){
9053                     Roo.fly(dom, '_internal').removeClass(className);
9054                     d.removeListener("mouseup", fn);
9055                 };
9056                 d.on("mouseup", fn);
9057             });
9058             return this;
9059         },
9060
9061         /**
9062          * Stops the specified event from bubbling and optionally prevents the default action
9063          * @param {String} eventName
9064          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9065          * @return {Roo.Element} this
9066          */
9067         swallowEvent : function(eventName, preventDefault){
9068             var fn = function(e){
9069                 e.stopPropagation();
9070                 if(preventDefault){
9071                     e.preventDefault();
9072                 }
9073             };
9074             if(eventName instanceof Array){
9075                 for(var i = 0, len = eventName.length; i < len; i++){
9076                      this.on(eventName[i], fn);
9077                 }
9078                 return this;
9079             }
9080             this.on(eventName, fn);
9081             return this;
9082         },
9083
9084         /**
9085          * @private
9086          */
9087       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9088
9089         /**
9090          * Sizes this element to its parent element's dimensions performing
9091          * neccessary box adjustments.
9092          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9093          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9094          * @return {Roo.Element} this
9095          */
9096         fitToParent : function(monitorResize, targetParent) {
9097           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9098           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9099           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9100             return;
9101           }
9102           var p = Roo.get(targetParent || this.dom.parentNode);
9103           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9104           if (monitorResize === true) {
9105             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9106             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9107           }
9108           return this;
9109         },
9110
9111         /**
9112          * Gets the next sibling, skipping text nodes
9113          * @return {HTMLElement} The next sibling or null
9114          */
9115         getNextSibling : function(){
9116             var n = this.dom.nextSibling;
9117             while(n && n.nodeType != 1){
9118                 n = n.nextSibling;
9119             }
9120             return n;
9121         },
9122
9123         /**
9124          * Gets the previous sibling, skipping text nodes
9125          * @return {HTMLElement} The previous sibling or null
9126          */
9127         getPrevSibling : function(){
9128             var n = this.dom.previousSibling;
9129             while(n && n.nodeType != 1){
9130                 n = n.previousSibling;
9131             }
9132             return n;
9133         },
9134
9135
9136         /**
9137          * Appends the passed element(s) to this element
9138          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9139          * @return {Roo.Element} this
9140          */
9141         appendChild: function(el){
9142             el = Roo.get(el);
9143             el.appendTo(this);
9144             return this;
9145         },
9146
9147         /**
9148          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9149          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9150          * automatically generated with the specified attributes.
9151          * @param {HTMLElement} insertBefore (optional) a child element of this element
9152          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9153          * @return {Roo.Element} The new child element
9154          */
9155         createChild: function(config, insertBefore, returnDom){
9156             config = config || {tag:'div'};
9157             if(insertBefore){
9158                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9159             }
9160             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9161         },
9162
9163         /**
9164          * Appends this element to the passed element
9165          * @param {String/HTMLElement/Element} el The new parent element
9166          * @return {Roo.Element} this
9167          */
9168         appendTo: function(el){
9169             el = Roo.getDom(el);
9170             el.appendChild(this.dom);
9171             return this;
9172         },
9173
9174         /**
9175          * Inserts this element before the passed element in the DOM
9176          * @param {String/HTMLElement/Element} el The element to insert before
9177          * @return {Roo.Element} this
9178          */
9179         insertBefore: function(el){
9180             el = Roo.getDom(el);
9181             el.parentNode.insertBefore(this.dom, el);
9182             return this;
9183         },
9184
9185         /**
9186          * Inserts this element after the passed element in the DOM
9187          * @param {String/HTMLElement/Element} el The element to insert after
9188          * @return {Roo.Element} this
9189          */
9190         insertAfter: function(el){
9191             el = Roo.getDom(el);
9192             el.parentNode.insertBefore(this.dom, el.nextSibling);
9193             return this;
9194         },
9195
9196         /**
9197          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9198          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9199          * @return {Roo.Element} The new child
9200          */
9201         insertFirst: function(el, returnDom){
9202             el = el || {};
9203             if(typeof el == 'object' && !el.nodeType){ // dh config
9204                 return this.createChild(el, this.dom.firstChild, returnDom);
9205             }else{
9206                 el = Roo.getDom(el);
9207                 this.dom.insertBefore(el, this.dom.firstChild);
9208                 return !returnDom ? Roo.get(el) : el;
9209             }
9210         },
9211
9212         /**
9213          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9214          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9215          * @param {String} where (optional) 'before' or 'after' defaults to before
9216          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9217          * @return {Roo.Element} the inserted Element
9218          */
9219         insertSibling: function(el, where, returnDom){
9220             where = where ? where.toLowerCase() : 'before';
9221             el = el || {};
9222             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9223
9224             if(typeof el == 'object' && !el.nodeType){ // dh config
9225                 if(where == 'after' && !this.dom.nextSibling){
9226                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9227                 }else{
9228                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9229                 }
9230
9231             }else{
9232                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9233                             where == 'before' ? this.dom : this.dom.nextSibling);
9234                 if(!returnDom){
9235                     rt = Roo.get(rt);
9236                 }
9237             }
9238             return rt;
9239         },
9240
9241         /**
9242          * Creates and wraps this element with another element
9243          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {HTMLElement/Element} The newly created wrapper element
9246          */
9247         wrap: function(config, returnDom){
9248             if(!config){
9249                 config = {tag: "div"};
9250             }
9251             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9252             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9253             return newEl;
9254         },
9255
9256         /**
9257          * Replaces the passed element with this element
9258          * @param {String/HTMLElement/Element} el The element to replace
9259          * @return {Roo.Element} this
9260          */
9261         replace: function(el){
9262             el = Roo.get(el);
9263             this.insertBefore(el);
9264             el.remove();
9265             return this;
9266         },
9267
9268         /**
9269          * Inserts an html fragment into this element
9270          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9271          * @param {String} html The HTML fragment
9272          * @param {Boolean} returnEl True to return an Roo.Element
9273          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9274          */
9275         insertHtml : function(where, html, returnEl){
9276             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9277             return returnEl ? Roo.get(el) : el;
9278         },
9279
9280         /**
9281          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9282          * @param {Object} o The object with the attributes
9283          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9284          * @return {Roo.Element} this
9285          */
9286         set : function(o, useSet){
9287             var el = this.dom;
9288             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9289             for(var attr in o){
9290                 if(attr == "style" || typeof o[attr] == "function") continue;
9291                 if(attr=="cls"){
9292                     el.className = o["cls"];
9293                 }else{
9294                     if(useSet) el.setAttribute(attr, o[attr]);
9295                     else el[attr] = o[attr];
9296                 }
9297             }
9298             if(o.style){
9299                 Roo.DomHelper.applyStyles(el, o.style);
9300             }
9301             return this;
9302         },
9303
9304         /**
9305          * Convenience method for constructing a KeyMap
9306          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9307          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9308          * @param {Function} fn The function to call
9309          * @param {Object} scope (optional) The scope of the function
9310          * @return {Roo.KeyMap} The KeyMap created
9311          */
9312         addKeyListener : function(key, fn, scope){
9313             var config;
9314             if(typeof key != "object" || key instanceof Array){
9315                 config = {
9316                     key: key,
9317                     fn: fn,
9318                     scope: scope
9319                 };
9320             }else{
9321                 config = {
9322                     key : key.key,
9323                     shift : key.shift,
9324                     ctrl : key.ctrl,
9325                     alt : key.alt,
9326                     fn: fn,
9327                     scope: scope
9328                 };
9329             }
9330             return new Roo.KeyMap(this, config);
9331         },
9332
9333         /**
9334          * Creates a KeyMap for this element
9335          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9336          * @return {Roo.KeyMap} The KeyMap created
9337          */
9338         addKeyMap : function(config){
9339             return new Roo.KeyMap(this, config);
9340         },
9341
9342         /**
9343          * Returns true if this element is scrollable.
9344          * @return {Boolean}
9345          */
9346          isScrollable : function(){
9347             var dom = this.dom;
9348             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9349         },
9350
9351         /**
9352          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9353          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9354          * @param {Number} value The new scroll value
9355          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9356          * @return {Element} this
9357          */
9358
9359         scrollTo : function(side, value, animate){
9360             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9361             if(!animate || !A){
9362                 this.dom[prop] = value;
9363             }else{
9364                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9365                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9366             }
9367             return this;
9368         },
9369
9370         /**
9371          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9372          * within this element's scrollable range.
9373          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9374          * @param {Number} distance How far to scroll the element in pixels
9375          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9376          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9377          * was scrolled as far as it could go.
9378          */
9379          scroll : function(direction, distance, animate){
9380              if(!this.isScrollable()){
9381                  return;
9382              }
9383              var el = this.dom;
9384              var l = el.scrollLeft, t = el.scrollTop;
9385              var w = el.scrollWidth, h = el.scrollHeight;
9386              var cw = el.clientWidth, ch = el.clientHeight;
9387              direction = direction.toLowerCase();
9388              var scrolled = false;
9389              var a = this.preanim(arguments, 2);
9390              switch(direction){
9391                  case "l":
9392                  case "left":
9393                      if(w - l > cw){
9394                          var v = Math.min(l + distance, w-cw);
9395                          this.scrollTo("left", v, a);
9396                          scrolled = true;
9397                      }
9398                      break;
9399                 case "r":
9400                 case "right":
9401                      if(l > 0){
9402                          var v = Math.max(l - distance, 0);
9403                          this.scrollTo("left", v, a);
9404                          scrolled = true;
9405                      }
9406                      break;
9407                 case "t":
9408                 case "top":
9409                 case "up":
9410                      if(t > 0){
9411                          var v = Math.max(t - distance, 0);
9412                          this.scrollTo("top", v, a);
9413                          scrolled = true;
9414                      }
9415                      break;
9416                 case "b":
9417                 case "bottom":
9418                 case "down":
9419                      if(h - t > ch){
9420                          var v = Math.min(t + distance, h-ch);
9421                          this.scrollTo("top", v, a);
9422                          scrolled = true;
9423                      }
9424                      break;
9425              }
9426              return scrolled;
9427         },
9428
9429         /**
9430          * Translates the passed page coordinates into left/top css values for this element
9431          * @param {Number/Array} x The page x or an array containing [x, y]
9432          * @param {Number} y The page y
9433          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9434          */
9435         translatePoints : function(x, y){
9436             if(typeof x == 'object' || x instanceof Array){
9437                 y = x[1]; x = x[0];
9438             }
9439             var p = this.getStyle('position');
9440             var o = this.getXY();
9441
9442             var l = parseInt(this.getStyle('left'), 10);
9443             var t = parseInt(this.getStyle('top'), 10);
9444
9445             if(isNaN(l)){
9446                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9447             }
9448             if(isNaN(t)){
9449                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9450             }
9451
9452             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9453         },
9454
9455         /**
9456          * Returns the current scroll position of the element.
9457          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9458          */
9459         getScroll : function(){
9460             var d = this.dom, doc = document;
9461             if(d == doc || d == doc.body){
9462                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9463                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9464                 return {left: l, top: t};
9465             }else{
9466                 return {left: d.scrollLeft, top: d.scrollTop};
9467             }
9468         },
9469
9470         /**
9471          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9472          * are convert to standard 6 digit hex color.
9473          * @param {String} attr The css attribute
9474          * @param {String} defaultValue The default value to use when a valid color isn't found
9475          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9476          * YUI color anims.
9477          */
9478         getColor : function(attr, defaultValue, prefix){
9479             var v = this.getStyle(attr);
9480             if(!v || v == "transparent" || v == "inherit") {
9481                 return defaultValue;
9482             }
9483             var color = typeof prefix == "undefined" ? "#" : prefix;
9484             if(v.substr(0, 4) == "rgb("){
9485                 var rvs = v.slice(4, v.length -1).split(",");
9486                 for(var i = 0; i < 3; i++){
9487                     var h = parseInt(rvs[i]).toString(16);
9488                     if(h < 16){
9489                         h = "0" + h;
9490                     }
9491                     color += h;
9492                 }
9493             } else {
9494                 if(v.substr(0, 1) == "#"){
9495                     if(v.length == 4) {
9496                         for(var i = 1; i < 4; i++){
9497                             var c = v.charAt(i);
9498                             color +=  c + c;
9499                         }
9500                     }else if(v.length == 7){
9501                         color += v.substr(1);
9502                     }
9503                 }
9504             }
9505             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9506         },
9507
9508         /**
9509          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9510          * gradient background, rounded corners and a 4-way shadow.
9511          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9512          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9513          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9514          * @return {Roo.Element} this
9515          */
9516         boxWrap : function(cls){
9517             cls = cls || 'x-box';
9518             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9519             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9520             return el;
9521         },
9522
9523         /**
9524          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9525          * @param {String} namespace The namespace in which to look for the attribute
9526          * @param {String} name The attribute name
9527          * @return {String} The attribute value
9528          */
9529         getAttributeNS : Roo.isIE ? function(ns, name){
9530             var d = this.dom;
9531             var type = typeof d[ns+":"+name];
9532             if(type != 'undefined' && type != 'unknown'){
9533                 return d[ns+":"+name];
9534             }
9535             return d[name];
9536         } : function(ns, name){
9537             var d = this.dom;
9538             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9539         }
9540     };
9541
9542     var ep = El.prototype;
9543
9544     /**
9545      * Appends an event handler (Shorthand for addListener)
9546      * @param {String}   eventName     The type of event to append
9547      * @param {Function} fn        The method the event invokes
9548      * @param {Object} scope       (optional) The scope (this object) of the fn
9549      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9550      * @method
9551      */
9552     ep.on = ep.addListener;
9553         // backwards compat
9554     ep.mon = ep.addListener;
9555
9556     /**
9557      * Removes an event handler from this element (shorthand for removeListener)
9558      * @param {String} eventName the type of event to remove
9559      * @param {Function} fn the method the event invokes
9560      * @return {Roo.Element} this
9561      * @method
9562      */
9563     ep.un = ep.removeListener;
9564
9565     /**
9566      * true to automatically adjust width and height settings for box-model issues (default to true)
9567      */
9568     ep.autoBoxAdjust = true;
9569
9570     // private
9571     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9572
9573     // private
9574     El.addUnits = function(v, defaultUnit){
9575         if(v === "" || v == "auto"){
9576             return v;
9577         }
9578         if(v === undefined){
9579             return '';
9580         }
9581         if(typeof v == "number" || !El.unitPattern.test(v)){
9582             return v + (defaultUnit || 'px');
9583         }
9584         return v;
9585     };
9586
9587     // special markup used throughout Roo when box wrapping elements
9588     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9589     /**
9590      * Visibility mode constant - Use visibility to hide element
9591      * @static
9592      * @type Number
9593      */
9594     El.VISIBILITY = 1;
9595     /**
9596      * Visibility mode constant - Use display to hide element
9597      * @static
9598      * @type Number
9599      */
9600     El.DISPLAY = 2;
9601
9602     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9603     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9604     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9605
9606
9607
9608     /**
9609      * @private
9610      */
9611     El.cache = {};
9612
9613     var docEl;
9614
9615     /**
9616      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9617      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9618      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9619      * @return {Element} The Element object
9620      * @static
9621      */
9622     El.get = function(el){
9623         var ex, elm, id;
9624         if(!el){ return null; }
9625         if(typeof el == "string"){ // element id
9626             if(!(elm = document.getElementById(el))){
9627                 return null;
9628             }
9629             if(ex = El.cache[el]){
9630                 ex.dom = elm;
9631             }else{
9632                 ex = El.cache[el] = new El(elm);
9633             }
9634             return ex;
9635         }else if(el.tagName){ // dom element
9636             if(!(id = el.id)){
9637                 id = Roo.id(el);
9638             }
9639             if(ex = El.cache[id]){
9640                 ex.dom = el;
9641             }else{
9642                 ex = El.cache[id] = new El(el);
9643             }
9644             return ex;
9645         }else if(el instanceof El){
9646             if(el != docEl){
9647                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9648                                                               // catch case where it hasn't been appended
9649                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9650             }
9651             return el;
9652         }else if(el.isComposite){
9653             return el;
9654         }else if(el instanceof Array){
9655             return El.select(el);
9656         }else if(el == document){
9657             // create a bogus element object representing the document object
9658             if(!docEl){
9659                 var f = function(){};
9660                 f.prototype = El.prototype;
9661                 docEl = new f();
9662                 docEl.dom = document;
9663             }
9664             return docEl;
9665         }
9666         return null;
9667     };
9668
9669     // private
9670     El.uncache = function(el){
9671         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9672             if(a[i]){
9673                 delete El.cache[a[i].id || a[i]];
9674             }
9675         }
9676     };
9677
9678     // private
9679     // Garbage collection - uncache elements/purge listeners on orphaned elements
9680     // so we don't hold a reference and cause the browser to retain them
9681     El.garbageCollect = function(){
9682         if(!Roo.enableGarbageCollector){
9683             clearInterval(El.collectorThread);
9684             return;
9685         }
9686         for(var eid in El.cache){
9687             var el = El.cache[eid], d = el.dom;
9688             // -------------------------------------------------------
9689             // Determining what is garbage:
9690             // -------------------------------------------------------
9691             // !d
9692             // dom node is null, definitely garbage
9693             // -------------------------------------------------------
9694             // !d.parentNode
9695             // no parentNode == direct orphan, definitely garbage
9696             // -------------------------------------------------------
9697             // !d.offsetParent && !document.getElementById(eid)
9698             // display none elements have no offsetParent so we will
9699             // also try to look it up by it's id. However, check
9700             // offsetParent first so we don't do unneeded lookups.
9701             // This enables collection of elements that are not orphans
9702             // directly, but somewhere up the line they have an orphan
9703             // parent.
9704             // -------------------------------------------------------
9705             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9706                 delete El.cache[eid];
9707                 if(d && Roo.enableListenerCollection){
9708                     E.purgeElement(d);
9709                 }
9710             }
9711         }
9712     }
9713     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9714
9715
9716     // dom is optional
9717     El.Flyweight = function(dom){
9718         this.dom = dom;
9719     };
9720     El.Flyweight.prototype = El.prototype;
9721
9722     El._flyweights = {};
9723     /**
9724      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9725      * the dom node can be overwritten by other code.
9726      * @param {String/HTMLElement} el The dom node or id
9727      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9728      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9729      * @static
9730      * @return {Element} The shared Element object
9731      */
9732     El.fly = function(el, named){
9733         named = named || '_global';
9734         el = Roo.getDom(el);
9735         if(!el){
9736             return null;
9737         }
9738         if(!El._flyweights[named]){
9739             El._flyweights[named] = new El.Flyweight();
9740         }
9741         El._flyweights[named].dom = el;
9742         return El._flyweights[named];
9743     };
9744
9745     /**
9746      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9747      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9748      * Shorthand of {@link Roo.Element#get}
9749      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9750      * @return {Element} The Element object
9751      * @member Roo
9752      * @method get
9753      */
9754     Roo.get = El.get;
9755     /**
9756      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9757      * the dom node can be overwritten by other code.
9758      * Shorthand of {@link Roo.Element#fly}
9759      * @param {String/HTMLElement} el The dom node or id
9760      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9761      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9762      * @static
9763      * @return {Element} The shared Element object
9764      * @member Roo
9765      * @method fly
9766      */
9767     Roo.fly = El.fly;
9768
9769     // speedy lookup for elements never to box adjust
9770     var noBoxAdjust = Roo.isStrict ? {
9771         select:1
9772     } : {
9773         input:1, select:1, textarea:1
9774     };
9775     if(Roo.isIE || Roo.isGecko){
9776         noBoxAdjust['button'] = 1;
9777     }
9778
9779
9780     Roo.EventManager.on(window, 'unload', function(){
9781         delete El.cache;
9782         delete El._flyweights;
9783     });
9784 })();
9785
9786
9787
9788
9789 if(Roo.DomQuery){
9790     Roo.Element.selectorFunction = Roo.DomQuery.select;
9791 }
9792
9793 Roo.Element.select = function(selector, unique, root){
9794     var els;
9795     if(typeof selector == "string"){
9796         els = Roo.Element.selectorFunction(selector, root);
9797     }else if(selector.length !== undefined){
9798         els = selector;
9799     }else{
9800         throw "Invalid selector";
9801     }
9802     if(unique === true){
9803         return new Roo.CompositeElement(els);
9804     }else{
9805         return new Roo.CompositeElementLite(els);
9806     }
9807 };
9808 /**
9809  * Selects elements based on the passed CSS selector to enable working on them as 1.
9810  * @param {String/Array} selector The CSS selector or an array of elements
9811  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9812  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9813  * @return {CompositeElementLite/CompositeElement}
9814  * @member Roo
9815  * @method select
9816  */
9817 Roo.select = Roo.Element.select;
9818
9819
9820
9821
9822
9823
9824
9825
9826
9827
9828
9829
9830
9831
9832 /*
9833  * Based on:
9834  * Ext JS Library 1.1.1
9835  * Copyright(c) 2006-2007, Ext JS, LLC.
9836  *
9837  * Originally Released Under LGPL - original licence link has changed is not relivant.
9838  *
9839  * Fork - LGPL
9840  * <script type="text/javascript">
9841  */
9842
9843
9844
9845 //Notifies Element that fx methods are available
9846 Roo.enableFx = true;
9847
9848 /**
9849  * @class Roo.Fx
9850  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9851  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9852  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9853  * Element effects to work.</p><br/>
9854  *
9855  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9856  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9857  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9858  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9859  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9860  * expected results and should be done with care.</p><br/>
9861  *
9862  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9863  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9864 <pre>
9865 Value  Description
9866 -----  -----------------------------
9867 tl     The top left corner
9868 t      The center of the top edge
9869 tr     The top right corner
9870 l      The center of the left edge
9871 r      The center of the right edge
9872 bl     The bottom left corner
9873 b      The center of the bottom edge
9874 br     The bottom right corner
9875 </pre>
9876  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9877  * below are common options that can be passed to any Fx method.</b>
9878  * @cfg {Function} callback A function called when the effect is finished
9879  * @cfg {Object} scope The scope of the effect function
9880  * @cfg {String} easing A valid Easing value for the effect
9881  * @cfg {String} afterCls A css class to apply after the effect
9882  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9883  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9884  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9885  * effects that end with the element being visually hidden, ignored otherwise)
9886  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9887  * a function which returns such a specification that will be applied to the Element after the effect finishes
9888  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9889  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9890  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9891  */
9892 Roo.Fx = {
9893         /**
9894          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9895          * origin for the slide effect.  This function automatically handles wrapping the element with
9896          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9897          * Usage:
9898          *<pre><code>
9899 // default: slide the element in from the top
9900 el.slideIn();
9901
9902 // custom: slide the element in from the right with a 2-second duration
9903 el.slideIn('r', { duration: 2 });
9904
9905 // common config options shown with default values
9906 el.slideIn('t', {
9907     easing: 'easeOut',
9908     duration: .5
9909 });
9910 </code></pre>
9911          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9912          * @param {Object} options (optional) Object literal with any of the Fx config options
9913          * @return {Roo.Element} The Element
9914          */
9915     slideIn : function(anchor, o){
9916         var el = this.getFxEl();
9917         o = o || {};
9918
9919         el.queueFx(o, function(){
9920
9921             anchor = anchor || "t";
9922
9923             // fix display to visibility
9924             this.fixDisplay();
9925
9926             // restore values after effect
9927             var r = this.getFxRestore();
9928             var b = this.getBox();
9929             // fixed size for slide
9930             this.setSize(b);
9931
9932             // wrap if needed
9933             var wrap = this.fxWrap(r.pos, o, "hidden");
9934
9935             var st = this.dom.style;
9936             st.visibility = "visible";
9937             st.position = "absolute";
9938
9939             // clear out temp styles after slide and unwrap
9940             var after = function(){
9941                 el.fxUnwrap(wrap, r.pos, o);
9942                 st.width = r.width;
9943                 st.height = r.height;
9944                 el.afterFx(o);
9945             };
9946             // time to calc the positions
9947             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9948
9949             switch(anchor.toLowerCase()){
9950                 case "t":
9951                     wrap.setSize(b.width, 0);
9952                     st.left = st.bottom = "0";
9953                     a = {height: bh};
9954                 break;
9955                 case "l":
9956                     wrap.setSize(0, b.height);
9957                     st.right = st.top = "0";
9958                     a = {width: bw};
9959                 break;
9960                 case "r":
9961                     wrap.setSize(0, b.height);
9962                     wrap.setX(b.right);
9963                     st.left = st.top = "0";
9964                     a = {width: bw, points: pt};
9965                 break;
9966                 case "b":
9967                     wrap.setSize(b.width, 0);
9968                     wrap.setY(b.bottom);
9969                     st.left = st.top = "0";
9970                     a = {height: bh, points: pt};
9971                 break;
9972                 case "tl":
9973                     wrap.setSize(0, 0);
9974                     st.right = st.bottom = "0";
9975                     a = {width: bw, height: bh};
9976                 break;
9977                 case "bl":
9978                     wrap.setSize(0, 0);
9979                     wrap.setY(b.y+b.height);
9980                     st.right = st.top = "0";
9981                     a = {width: bw, height: bh, points: pt};
9982                 break;
9983                 case "br":
9984                     wrap.setSize(0, 0);
9985                     wrap.setXY([b.right, b.bottom]);
9986                     st.left = st.top = "0";
9987                     a = {width: bw, height: bh, points: pt};
9988                 break;
9989                 case "tr":
9990                     wrap.setSize(0, 0);
9991                     wrap.setX(b.x+b.width);
9992                     st.left = st.bottom = "0";
9993                     a = {width: bw, height: bh, points: pt};
9994                 break;
9995             }
9996             this.dom.style.visibility = "visible";
9997             wrap.show();
9998
9999             arguments.callee.anim = wrap.fxanim(a,
10000                 o,
10001                 'motion',
10002                 .5,
10003                 'easeOut', after);
10004         });
10005         return this;
10006     },
10007     
10008         /**
10009          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10010          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10011          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10012          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10013          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10014          * Usage:
10015          *<pre><code>
10016 // default: slide the element out to the top
10017 el.slideOut();
10018
10019 // custom: slide the element out to the right with a 2-second duration
10020 el.slideOut('r', { duration: 2 });
10021
10022 // common config options shown with default values
10023 el.slideOut('t', {
10024     easing: 'easeOut',
10025     duration: .5,
10026     remove: false,
10027     useDisplay: false
10028 });
10029 </code></pre>
10030          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10031          * @param {Object} options (optional) Object literal with any of the Fx config options
10032          * @return {Roo.Element} The Element
10033          */
10034     slideOut : function(anchor, o){
10035         var el = this.getFxEl();
10036         o = o || {};
10037
10038         el.queueFx(o, function(){
10039
10040             anchor = anchor || "t";
10041
10042             // restore values after effect
10043             var r = this.getFxRestore();
10044             
10045             var b = this.getBox();
10046             // fixed size for slide
10047             this.setSize(b);
10048
10049             // wrap if needed
10050             var wrap = this.fxWrap(r.pos, o, "visible");
10051
10052             var st = this.dom.style;
10053             st.visibility = "visible";
10054             st.position = "absolute";
10055
10056             wrap.setSize(b);
10057
10058             var after = function(){
10059                 if(o.useDisplay){
10060                     el.setDisplayed(false);
10061                 }else{
10062                     el.hide();
10063                 }
10064
10065                 el.fxUnwrap(wrap, r.pos, o);
10066
10067                 st.width = r.width;
10068                 st.height = r.height;
10069
10070                 el.afterFx(o);
10071             };
10072
10073             var a, zero = {to: 0};
10074             switch(anchor.toLowerCase()){
10075                 case "t":
10076                     st.left = st.bottom = "0";
10077                     a = {height: zero};
10078                 break;
10079                 case "l":
10080                     st.right = st.top = "0";
10081                     a = {width: zero};
10082                 break;
10083                 case "r":
10084                     st.left = st.top = "0";
10085                     a = {width: zero, points: {to:[b.right, b.y]}};
10086                 break;
10087                 case "b":
10088                     st.left = st.top = "0";
10089                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10090                 break;
10091                 case "tl":
10092                     st.right = st.bottom = "0";
10093                     a = {width: zero, height: zero};
10094                 break;
10095                 case "bl":
10096                     st.right = st.top = "0";
10097                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10098                 break;
10099                 case "br":
10100                     st.left = st.top = "0";
10101                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10102                 break;
10103                 case "tr":
10104                     st.left = st.bottom = "0";
10105                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10106                 break;
10107             }
10108
10109             arguments.callee.anim = wrap.fxanim(a,
10110                 o,
10111                 'motion',
10112                 .5,
10113                 "easeOut", after);
10114         });
10115         return this;
10116     },
10117
10118         /**
10119          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10120          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10121          * The element must be removed from the DOM using the 'remove' config option if desired.
10122          * Usage:
10123          *<pre><code>
10124 // default
10125 el.puff();
10126
10127 // common config options shown with default values
10128 el.puff({
10129     easing: 'easeOut',
10130     duration: .5,
10131     remove: false,
10132     useDisplay: false
10133 });
10134 </code></pre>
10135          * @param {Object} options (optional) Object literal with any of the Fx config options
10136          * @return {Roo.Element} The Element
10137          */
10138     puff : function(o){
10139         var el = this.getFxEl();
10140         o = o || {};
10141
10142         el.queueFx(o, function(){
10143             this.clearOpacity();
10144             this.show();
10145
10146             // restore values after effect
10147             var r = this.getFxRestore();
10148             var st = this.dom.style;
10149
10150             var after = function(){
10151                 if(o.useDisplay){
10152                     el.setDisplayed(false);
10153                 }else{
10154                     el.hide();
10155                 }
10156
10157                 el.clearOpacity();
10158
10159                 el.setPositioning(r.pos);
10160                 st.width = r.width;
10161                 st.height = r.height;
10162                 st.fontSize = '';
10163                 el.afterFx(o);
10164             };
10165
10166             var width = this.getWidth();
10167             var height = this.getHeight();
10168
10169             arguments.callee.anim = this.fxanim({
10170                     width : {to: this.adjustWidth(width * 2)},
10171                     height : {to: this.adjustHeight(height * 2)},
10172                     points : {by: [-(width * .5), -(height * .5)]},
10173                     opacity : {to: 0},
10174                     fontSize: {to:200, unit: "%"}
10175                 },
10176                 o,
10177                 'motion',
10178                 .5,
10179                 "easeOut", after);
10180         });
10181         return this;
10182     },
10183
10184         /**
10185          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10186          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10187          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10188          * Usage:
10189          *<pre><code>
10190 // default
10191 el.switchOff();
10192
10193 // all config options shown with default values
10194 el.switchOff({
10195     easing: 'easeIn',
10196     duration: .3,
10197     remove: false,
10198     useDisplay: false
10199 });
10200 </code></pre>
10201          * @param {Object} options (optional) Object literal with any of the Fx config options
10202          * @return {Roo.Element} The Element
10203          */
10204     switchOff : function(o){
10205         var el = this.getFxEl();
10206         o = o || {};
10207
10208         el.queueFx(o, function(){
10209             this.clearOpacity();
10210             this.clip();
10211
10212             // restore values after effect
10213             var r = this.getFxRestore();
10214             var st = this.dom.style;
10215
10216             var after = function(){
10217                 if(o.useDisplay){
10218                     el.setDisplayed(false);
10219                 }else{
10220                     el.hide();
10221                 }
10222
10223                 el.clearOpacity();
10224                 el.setPositioning(r.pos);
10225                 st.width = r.width;
10226                 st.height = r.height;
10227
10228                 el.afterFx(o);
10229             };
10230
10231             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10232                 this.clearOpacity();
10233                 (function(){
10234                     this.fxanim({
10235                         height:{to:1},
10236                         points:{by:[0, this.getHeight() * .5]}
10237                     }, o, 'motion', 0.3, 'easeIn', after);
10238                 }).defer(100, this);
10239             });
10240         });
10241         return this;
10242     },
10243
10244     /**
10245      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10246      * changed using the "attr" config option) and then fading back to the original color. If no original
10247      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10248      * Usage:
10249 <pre><code>
10250 // default: highlight background to yellow
10251 el.highlight();
10252
10253 // custom: highlight foreground text to blue for 2 seconds
10254 el.highlight("0000ff", { attr: 'color', duration: 2 });
10255
10256 // common config options shown with default values
10257 el.highlight("ffff9c", {
10258     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10259     endColor: (current color) or "ffffff",
10260     easing: 'easeIn',
10261     duration: 1
10262 });
10263 </code></pre>
10264      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10265      * @param {Object} options (optional) Object literal with any of the Fx config options
10266      * @return {Roo.Element} The Element
10267      */ 
10268     highlight : function(color, o){
10269         var el = this.getFxEl();
10270         o = o || {};
10271
10272         el.queueFx(o, function(){
10273             color = color || "ffff9c";
10274             attr = o.attr || "backgroundColor";
10275
10276             this.clearOpacity();
10277             this.show();
10278
10279             var origColor = this.getColor(attr);
10280             var restoreColor = this.dom.style[attr];
10281             endColor = (o.endColor || origColor) || "ffffff";
10282
10283             var after = function(){
10284                 el.dom.style[attr] = restoreColor;
10285                 el.afterFx(o);
10286             };
10287
10288             var a = {};
10289             a[attr] = {from: color, to: endColor};
10290             arguments.callee.anim = this.fxanim(a,
10291                 o,
10292                 'color',
10293                 1,
10294                 'easeIn', after);
10295         });
10296         return this;
10297     },
10298
10299    /**
10300     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10301     * Usage:
10302 <pre><code>
10303 // default: a single light blue ripple
10304 el.frame();
10305
10306 // custom: 3 red ripples lasting 3 seconds total
10307 el.frame("ff0000", 3, { duration: 3 });
10308
10309 // common config options shown with default values
10310 el.frame("C3DAF9", 1, {
10311     duration: 1 //duration of entire animation (not each individual ripple)
10312     // Note: Easing is not configurable and will be ignored if included
10313 });
10314 </code></pre>
10315     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10316     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10317     * @param {Object} options (optional) Object literal with any of the Fx config options
10318     * @return {Roo.Element} The Element
10319     */
10320     frame : function(color, count, o){
10321         var el = this.getFxEl();
10322         o = o || {};
10323
10324         el.queueFx(o, function(){
10325             color = color || "#C3DAF9";
10326             if(color.length == 6){
10327                 color = "#" + color;
10328             }
10329             count = count || 1;
10330             duration = o.duration || 1;
10331             this.show();
10332
10333             var b = this.getBox();
10334             var animFn = function(){
10335                 var proxy = this.createProxy({
10336
10337                      style:{
10338                         visbility:"hidden",
10339                         position:"absolute",
10340                         "z-index":"35000", // yee haw
10341                         border:"0px solid " + color
10342                      }
10343                   });
10344                 var scale = Roo.isBorderBox ? 2 : 1;
10345                 proxy.animate({
10346                     top:{from:b.y, to:b.y - 20},
10347                     left:{from:b.x, to:b.x - 20},
10348                     borderWidth:{from:0, to:10},
10349                     opacity:{from:1, to:0},
10350                     height:{from:b.height, to:(b.height + (20*scale))},
10351                     width:{from:b.width, to:(b.width + (20*scale))}
10352                 }, duration, function(){
10353                     proxy.remove();
10354                 });
10355                 if(--count > 0){
10356                      animFn.defer((duration/2)*1000, this);
10357                 }else{
10358                     el.afterFx(o);
10359                 }
10360             };
10361             animFn.call(this);
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Creates a pause before any subsequent queued effects begin.  If there are
10368     * no effects queued after the pause it will have no effect.
10369     * Usage:
10370 <pre><code>
10371 el.pause(1);
10372 </code></pre>
10373     * @param {Number} seconds The length of time to pause (in seconds)
10374     * @return {Roo.Element} The Element
10375     */
10376     pause : function(seconds){
10377         var el = this.getFxEl();
10378         var o = {};
10379
10380         el.queueFx(o, function(){
10381             setTimeout(function(){
10382                 el.afterFx(o);
10383             }, seconds * 1000);
10384         });
10385         return this;
10386     },
10387
10388    /**
10389     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10390     * using the "endOpacity" config option.
10391     * Usage:
10392 <pre><code>
10393 // default: fade in from opacity 0 to 100%
10394 el.fadeIn();
10395
10396 // custom: fade in from opacity 0 to 75% over 2 seconds
10397 el.fadeIn({ endOpacity: .75, duration: 2});
10398
10399 // common config options shown with default values
10400 el.fadeIn({
10401     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10402     easing: 'easeOut',
10403     duration: .5
10404 });
10405 </code></pre>
10406     * @param {Object} options (optional) Object literal with any of the Fx config options
10407     * @return {Roo.Element} The Element
10408     */
10409     fadeIn : function(o){
10410         var el = this.getFxEl();
10411         o = o || {};
10412         el.queueFx(o, function(){
10413             this.setOpacity(0);
10414             this.fixDisplay();
10415             this.dom.style.visibility = 'visible';
10416             var to = o.endOpacity || 1;
10417             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10418                 o, null, .5, "easeOut", function(){
10419                 if(to == 1){
10420                     this.clearOpacity();
10421                 }
10422                 el.afterFx(o);
10423             });
10424         });
10425         return this;
10426     },
10427
10428    /**
10429     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10430     * using the "endOpacity" config option.
10431     * Usage:
10432 <pre><code>
10433 // default: fade out from the element's current opacity to 0
10434 el.fadeOut();
10435
10436 // custom: fade out from the element's current opacity to 25% over 2 seconds
10437 el.fadeOut({ endOpacity: .25, duration: 2});
10438
10439 // common config options shown with default values
10440 el.fadeOut({
10441     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10442     easing: 'easeOut',
10443     duration: .5
10444     remove: false,
10445     useDisplay: false
10446 });
10447 </code></pre>
10448     * @param {Object} options (optional) Object literal with any of the Fx config options
10449     * @return {Roo.Element} The Element
10450     */
10451     fadeOut : function(o){
10452         var el = this.getFxEl();
10453         o = o || {};
10454         el.queueFx(o, function(){
10455             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10456                 o, null, .5, "easeOut", function(){
10457                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10458                      this.dom.style.display = "none";
10459                 }else{
10460                      this.dom.style.visibility = "hidden";
10461                 }
10462                 this.clearOpacity();
10463                 el.afterFx(o);
10464             });
10465         });
10466         return this;
10467     },
10468
10469    /**
10470     * Animates the transition of an element's dimensions from a starting height/width
10471     * to an ending height/width.
10472     * Usage:
10473 <pre><code>
10474 // change height and width to 100x100 pixels
10475 el.scale(100, 100);
10476
10477 // common config options shown with default values.  The height and width will default to
10478 // the element's existing values if passed as null.
10479 el.scale(
10480     [element's width],
10481     [element's height], {
10482     easing: 'easeOut',
10483     duration: .35
10484 });
10485 </code></pre>
10486     * @param {Number} width  The new width (pass undefined to keep the original width)
10487     * @param {Number} height  The new height (pass undefined to keep the original height)
10488     * @param {Object} options (optional) Object literal with any of the Fx config options
10489     * @return {Roo.Element} The Element
10490     */
10491     scale : function(w, h, o){
10492         this.shift(Roo.apply({}, o, {
10493             width: w,
10494             height: h
10495         }));
10496         return this;
10497     },
10498
10499    /**
10500     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10501     * Any of these properties not specified in the config object will not be changed.  This effect 
10502     * requires that at least one new dimension, position or opacity setting must be passed in on
10503     * the config object in order for the function to have any effect.
10504     * Usage:
10505 <pre><code>
10506 // slide the element horizontally to x position 200 while changing the height and opacity
10507 el.shift({ x: 200, height: 50, opacity: .8 });
10508
10509 // common config options shown with default values.
10510 el.shift({
10511     width: [element's width],
10512     height: [element's height],
10513     x: [element's x position],
10514     y: [element's y position],
10515     opacity: [element's opacity],
10516     easing: 'easeOut',
10517     duration: .35
10518 });
10519 </code></pre>
10520     * @param {Object} options  Object literal with any of the Fx config options
10521     * @return {Roo.Element} The Element
10522     */
10523     shift : function(o){
10524         var el = this.getFxEl();
10525         o = o || {};
10526         el.queueFx(o, function(){
10527             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10528             if(w !== undefined){
10529                 a.width = {to: this.adjustWidth(w)};
10530             }
10531             if(h !== undefined){
10532                 a.height = {to: this.adjustHeight(h)};
10533             }
10534             if(x !== undefined || y !== undefined){
10535                 a.points = {to: [
10536                     x !== undefined ? x : this.getX(),
10537                     y !== undefined ? y : this.getY()
10538                 ]};
10539             }
10540             if(op !== undefined){
10541                 a.opacity = {to: op};
10542             }
10543             if(o.xy !== undefined){
10544                 a.points = {to: o.xy};
10545             }
10546             arguments.callee.anim = this.fxanim(a,
10547                 o, 'motion', .35, "easeOut", function(){
10548                 el.afterFx(o);
10549             });
10550         });
10551         return this;
10552     },
10553
10554         /**
10555          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10556          * ending point of the effect.
10557          * Usage:
10558          *<pre><code>
10559 // default: slide the element downward while fading out
10560 el.ghost();
10561
10562 // custom: slide the element out to the right with a 2-second duration
10563 el.ghost('r', { duration: 2 });
10564
10565 // common config options shown with default values
10566 el.ghost('b', {
10567     easing: 'easeOut',
10568     duration: .5
10569     remove: false,
10570     useDisplay: false
10571 });
10572 </code></pre>
10573          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10574          * @param {Object} options (optional) Object literal with any of the Fx config options
10575          * @return {Roo.Element} The Element
10576          */
10577     ghost : function(anchor, o){
10578         var el = this.getFxEl();
10579         o = o || {};
10580
10581         el.queueFx(o, function(){
10582             anchor = anchor || "b";
10583
10584             // restore values after effect
10585             var r = this.getFxRestore();
10586             var w = this.getWidth(),
10587                 h = this.getHeight();
10588
10589             var st = this.dom.style;
10590
10591             var after = function(){
10592                 if(o.useDisplay){
10593                     el.setDisplayed(false);
10594                 }else{
10595                     el.hide();
10596                 }
10597
10598                 el.clearOpacity();
10599                 el.setPositioning(r.pos);
10600                 st.width = r.width;
10601                 st.height = r.height;
10602
10603                 el.afterFx(o);
10604             };
10605
10606             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10607             switch(anchor.toLowerCase()){
10608                 case "t":
10609                     pt.by = [0, -h];
10610                 break;
10611                 case "l":
10612                     pt.by = [-w, 0];
10613                 break;
10614                 case "r":
10615                     pt.by = [w, 0];
10616                 break;
10617                 case "b":
10618                     pt.by = [0, h];
10619                 break;
10620                 case "tl":
10621                     pt.by = [-w, -h];
10622                 break;
10623                 case "bl":
10624                     pt.by = [-w, h];
10625                 break;
10626                 case "br":
10627                     pt.by = [w, h];
10628                 break;
10629                 case "tr":
10630                     pt.by = [w, -h];
10631                 break;
10632             }
10633
10634             arguments.callee.anim = this.fxanim(a,
10635                 o,
10636                 'motion',
10637                 .5,
10638                 "easeOut", after);
10639         });
10640         return this;
10641     },
10642
10643         /**
10644          * Ensures that all effects queued after syncFx is called on the element are
10645          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10646          * @return {Roo.Element} The Element
10647          */
10648     syncFx : function(){
10649         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10650             block : false,
10651             concurrent : true,
10652             stopFx : false
10653         });
10654         return this;
10655     },
10656
10657         /**
10658          * Ensures that all effects queued after sequenceFx is called on the element are
10659          * run in sequence.  This is the opposite of {@link #syncFx}.
10660          * @return {Roo.Element} The Element
10661          */
10662     sequenceFx : function(){
10663         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10664             block : false,
10665             concurrent : false,
10666             stopFx : false
10667         });
10668         return this;
10669     },
10670
10671         /* @private */
10672     nextFx : function(){
10673         var ef = this.fxQueue[0];
10674         if(ef){
10675             ef.call(this);
10676         }
10677     },
10678
10679         /**
10680          * Returns true if the element has any effects actively running or queued, else returns false.
10681          * @return {Boolean} True if element has active effects, else false
10682          */
10683     hasActiveFx : function(){
10684         return this.fxQueue && this.fxQueue[0];
10685     },
10686
10687         /**
10688          * Stops any running effects and clears the element's internal effects queue if it contains
10689          * any additional effects that haven't started yet.
10690          * @return {Roo.Element} The Element
10691          */
10692     stopFx : function(){
10693         if(this.hasActiveFx()){
10694             var cur = this.fxQueue[0];
10695             if(cur && cur.anim && cur.anim.isAnimated()){
10696                 this.fxQueue = [cur]; // clear out others
10697                 cur.anim.stop(true);
10698             }
10699         }
10700         return this;
10701     },
10702
10703         /* @private */
10704     beforeFx : function(o){
10705         if(this.hasActiveFx() && !o.concurrent){
10706            if(o.stopFx){
10707                this.stopFx();
10708                return true;
10709            }
10710            return false;
10711         }
10712         return true;
10713     },
10714
10715         /**
10716          * Returns true if the element is currently blocking so that no other effect can be queued
10717          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10718          * used to ensure that an effect initiated by a user action runs to completion prior to the
10719          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10720          * @return {Boolean} True if blocking, else false
10721          */
10722     hasFxBlock : function(){
10723         var q = this.fxQueue;
10724         return q && q[0] && q[0].block;
10725     },
10726
10727         /* @private */
10728     queueFx : function(o, fn){
10729         if(!this.fxQueue){
10730             this.fxQueue = [];
10731         }
10732         if(!this.hasFxBlock()){
10733             Roo.applyIf(o, this.fxDefaults);
10734             if(!o.concurrent){
10735                 var run = this.beforeFx(o);
10736                 fn.block = o.block;
10737                 this.fxQueue.push(fn);
10738                 if(run){
10739                     this.nextFx();
10740                 }
10741             }else{
10742                 fn.call(this);
10743             }
10744         }
10745         return this;
10746     },
10747
10748         /* @private */
10749     fxWrap : function(pos, o, vis){
10750         var wrap;
10751         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10752             var wrapXY;
10753             if(o.fixPosition){
10754                 wrapXY = this.getXY();
10755             }
10756             var div = document.createElement("div");
10757             div.style.visibility = vis;
10758             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10759             wrap.setPositioning(pos);
10760             if(wrap.getStyle("position") == "static"){
10761                 wrap.position("relative");
10762             }
10763             this.clearPositioning('auto');
10764             wrap.clip();
10765             wrap.dom.appendChild(this.dom);
10766             if(wrapXY){
10767                 wrap.setXY(wrapXY);
10768             }
10769         }
10770         return wrap;
10771     },
10772
10773         /* @private */
10774     fxUnwrap : function(wrap, pos, o){
10775         this.clearPositioning();
10776         this.setPositioning(pos);
10777         if(!o.wrap){
10778             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10779             wrap.remove();
10780         }
10781     },
10782
10783         /* @private */
10784     getFxRestore : function(){
10785         var st = this.dom.style;
10786         return {pos: this.getPositioning(), width: st.width, height : st.height};
10787     },
10788
10789         /* @private */
10790     afterFx : function(o){
10791         if(o.afterStyle){
10792             this.applyStyles(o.afterStyle);
10793         }
10794         if(o.afterCls){
10795             this.addClass(o.afterCls);
10796         }
10797         if(o.remove === true){
10798             this.remove();
10799         }
10800         Roo.callback(o.callback, o.scope, [this]);
10801         if(!o.concurrent){
10802             this.fxQueue.shift();
10803             this.nextFx();
10804         }
10805     },
10806
10807         /* @private */
10808     getFxEl : function(){ // support for composite element fx
10809         return Roo.get(this.dom);
10810     },
10811
10812         /* @private */
10813     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10814         animType = animType || 'run';
10815         opt = opt || {};
10816         var anim = Roo.lib.Anim[animType](
10817             this.dom, args,
10818             (opt.duration || defaultDur) || .35,
10819             (opt.easing || defaultEase) || 'easeOut',
10820             function(){
10821                 Roo.callback(cb, this);
10822             },
10823             this
10824         );
10825         opt.anim = anim;
10826         return anim;
10827     }
10828 };
10829
10830 // backwords compat
10831 Roo.Fx.resize = Roo.Fx.scale;
10832
10833 //When included, Roo.Fx is automatically applied to Element so that all basic
10834 //effects are available directly via the Element API
10835 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10836  * Based on:
10837  * Ext JS Library 1.1.1
10838  * Copyright(c) 2006-2007, Ext JS, LLC.
10839  *
10840  * Originally Released Under LGPL - original licence link has changed is not relivant.
10841  *
10842  * Fork - LGPL
10843  * <script type="text/javascript">
10844  */
10845
10846
10847 /**
10848  * @class Roo.CompositeElement
10849  * Standard composite class. Creates a Roo.Element for every element in the collection.
10850  * <br><br>
10851  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10852  * actions will be performed on all the elements in this collection.</b>
10853  * <br><br>
10854  * All methods return <i>this</i> and can be chained.
10855  <pre><code>
10856  var els = Roo.select("#some-el div.some-class", true);
10857  // or select directly from an existing element
10858  var el = Roo.get('some-el');
10859  el.select('div.some-class', true);
10860
10861  els.setWidth(100); // all elements become 100 width
10862  els.hide(true); // all elements fade out and hide
10863  // or
10864  els.setWidth(100).hide(true);
10865  </code></pre>
10866  */
10867 Roo.CompositeElement = function(els){
10868     this.elements = [];
10869     this.addElements(els);
10870 };
10871 Roo.CompositeElement.prototype = {
10872     isComposite: true,
10873     addElements : function(els){
10874         if(!els) return this;
10875         if(typeof els == "string"){
10876             els = Roo.Element.selectorFunction(els);
10877         }
10878         var yels = this.elements;
10879         var index = yels.length-1;
10880         for(var i = 0, len = els.length; i < len; i++) {
10881                 yels[++index] = Roo.get(els[i]);
10882         }
10883         return this;
10884     },
10885
10886     /**
10887     * Clears this composite and adds the elements returned by the passed selector.
10888     * @param {String/Array} els A string CSS selector, an array of elements or an element
10889     * @return {CompositeElement} this
10890     */
10891     fill : function(els){
10892         this.elements = [];
10893         this.add(els);
10894         return this;
10895     },
10896
10897     /**
10898     * Filters this composite to only elements that match the passed selector.
10899     * @param {String} selector A string CSS selector
10900     * @return {CompositeElement} this
10901     */
10902     filter : function(selector){
10903         var els = [];
10904         this.each(function(el){
10905             if(el.is(selector)){
10906                 els[els.length] = el.dom;
10907             }
10908         });
10909         this.fill(els);
10910         return this;
10911     },
10912
10913     invoke : function(fn, args){
10914         var els = this.elements;
10915         for(var i = 0, len = els.length; i < len; i++) {
10916                 Roo.Element.prototype[fn].apply(els[i], args);
10917         }
10918         return this;
10919     },
10920     /**
10921     * Adds elements to this composite.
10922     * @param {String/Array} els A string CSS selector, an array of elements or an element
10923     * @return {CompositeElement} this
10924     */
10925     add : function(els){
10926         if(typeof els == "string"){
10927             this.addElements(Roo.Element.selectorFunction(els));
10928         }else if(els.length !== undefined){
10929             this.addElements(els);
10930         }else{
10931             this.addElements([els]);
10932         }
10933         return this;
10934     },
10935     /**
10936     * Calls the passed function passing (el, this, index) for each element in this composite.
10937     * @param {Function} fn The function to call
10938     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10939     * @return {CompositeElement} this
10940     */
10941     each : function(fn, scope){
10942         var els = this.elements;
10943         for(var i = 0, len = els.length; i < len; i++){
10944             if(fn.call(scope || els[i], els[i], this, i) === false) {
10945                 break;
10946             }
10947         }
10948         return this;
10949     },
10950
10951     /**
10952      * Returns the Element object at the specified index
10953      * @param {Number} index
10954      * @return {Roo.Element}
10955      */
10956     item : function(index){
10957         return this.elements[index] || null;
10958     },
10959
10960     /**
10961      * Returns the first Element
10962      * @return {Roo.Element}
10963      */
10964     first : function(){
10965         return this.item(0);
10966     },
10967
10968     /**
10969      * Returns the last Element
10970      * @return {Roo.Element}
10971      */
10972     last : function(){
10973         return this.item(this.elements.length-1);
10974     },
10975
10976     /**
10977      * Returns the number of elements in this composite
10978      * @return Number
10979      */
10980     getCount : function(){
10981         return this.elements.length;
10982     },
10983
10984     /**
10985      * Returns true if this composite contains the passed element
10986      * @return Boolean
10987      */
10988     contains : function(el){
10989         return this.indexOf(el) !== -1;
10990     },
10991
10992     /**
10993      * Returns true if this composite contains the passed element
10994      * @return Boolean
10995      */
10996     indexOf : function(el){
10997         return this.elements.indexOf(Roo.get(el));
10998     },
10999
11000
11001     /**
11002     * Removes the specified element(s).
11003     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11004     * or an array of any of those.
11005     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11006     * @return {CompositeElement} this
11007     */
11008     removeElement : function(el, removeDom){
11009         if(el instanceof Array){
11010             for(var i = 0, len = el.length; i < len; i++){
11011                 this.removeElement(el[i]);
11012             }
11013             return this;
11014         }
11015         var index = typeof el == 'number' ? el : this.indexOf(el);
11016         if(index !== -1){
11017             if(removeDom){
11018                 var d = this.elements[index];
11019                 if(d.dom){
11020                     d.remove();
11021                 }else{
11022                     d.parentNode.removeChild(d);
11023                 }
11024             }
11025             this.elements.splice(index, 1);
11026         }
11027         return this;
11028     },
11029
11030     /**
11031     * Replaces the specified element with the passed element.
11032     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11033     * to replace.
11034     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11035     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11036     * @return {CompositeElement} this
11037     */
11038     replaceElement : function(el, replacement, domReplace){
11039         var index = typeof el == 'number' ? el : this.indexOf(el);
11040         if(index !== -1){
11041             if(domReplace){
11042                 this.elements[index].replaceWith(replacement);
11043             }else{
11044                 this.elements.splice(index, 1, Roo.get(replacement))
11045             }
11046         }
11047         return this;
11048     },
11049
11050     /**
11051      * Removes all elements.
11052      */
11053     clear : function(){
11054         this.elements = [];
11055     }
11056 };
11057 (function(){
11058     Roo.CompositeElement.createCall = function(proto, fnName){
11059         if(!proto[fnName]){
11060             proto[fnName] = function(){
11061                 return this.invoke(fnName, arguments);
11062             };
11063         }
11064     };
11065     for(var fnName in Roo.Element.prototype){
11066         if(typeof Roo.Element.prototype[fnName] == "function"){
11067             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11068         }
11069     };
11070 })();
11071 /*
11072  * Based on:
11073  * Ext JS Library 1.1.1
11074  * Copyright(c) 2006-2007, Ext JS, LLC.
11075  *
11076  * Originally Released Under LGPL - original licence link has changed is not relivant.
11077  *
11078  * Fork - LGPL
11079  * <script type="text/javascript">
11080  */
11081
11082 /**
11083  * @class Roo.CompositeElementLite
11084  * @extends Roo.CompositeElement
11085  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11086  <pre><code>
11087  var els = Roo.select("#some-el div.some-class");
11088  // or select directly from an existing element
11089  var el = Roo.get('some-el');
11090  el.select('div.some-class');
11091
11092  els.setWidth(100); // all elements become 100 width
11093  els.hide(true); // all elements fade out and hide
11094  // or
11095  els.setWidth(100).hide(true);
11096  </code></pre><br><br>
11097  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11098  * actions will be performed on all the elements in this collection.</b>
11099  */
11100 Roo.CompositeElementLite = function(els){
11101     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11102     this.el = new Roo.Element.Flyweight();
11103 };
11104 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11105     addElements : function(els){
11106         if(els){
11107             if(els instanceof Array){
11108                 this.elements = this.elements.concat(els);
11109             }else{
11110                 var yels = this.elements;
11111                 var index = yels.length-1;
11112                 for(var i = 0, len = els.length; i < len; i++) {
11113                     yels[++index] = els[i];
11114                 }
11115             }
11116         }
11117         return this;
11118     },
11119     invoke : function(fn, args){
11120         var els = this.elements;
11121         var el = this.el;
11122         for(var i = 0, len = els.length; i < len; i++) {
11123             el.dom = els[i];
11124                 Roo.Element.prototype[fn].apply(el, args);
11125         }
11126         return this;
11127     },
11128     /**
11129      * Returns a flyweight Element of the dom element object at the specified index
11130      * @param {Number} index
11131      * @return {Roo.Element}
11132      */
11133     item : function(index){
11134         if(!this.elements[index]){
11135             return null;
11136         }
11137         this.el.dom = this.elements[index];
11138         return this.el;
11139     },
11140
11141     // fixes scope with flyweight
11142     addListener : function(eventName, handler, scope, opt){
11143         var els = this.elements;
11144         for(var i = 0, len = els.length; i < len; i++) {
11145             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11146         }
11147         return this;
11148     },
11149
11150     /**
11151     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11152     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11153     * a reference to the dom node, use el.dom.</b>
11154     * @param {Function} fn The function to call
11155     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11156     * @return {CompositeElement} this
11157     */
11158     each : function(fn, scope){
11159         var els = this.elements;
11160         var el = this.el;
11161         for(var i = 0, len = els.length; i < len; i++){
11162             el.dom = els[i];
11163                 if(fn.call(scope || el, el, this, i) === false){
11164                 break;
11165             }
11166         }
11167         return this;
11168     },
11169
11170     indexOf : function(el){
11171         return this.elements.indexOf(Roo.getDom(el));
11172     },
11173
11174     replaceElement : function(el, replacement, domReplace){
11175         var index = typeof el == 'number' ? el : this.indexOf(el);
11176         if(index !== -1){
11177             replacement = Roo.getDom(replacement);
11178             if(domReplace){
11179                 var d = this.elements[index];
11180                 d.parentNode.insertBefore(replacement, d);
11181                 d.parentNode.removeChild(d);
11182             }
11183             this.elements.splice(index, 1, replacement);
11184         }
11185         return this;
11186     }
11187 });
11188 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11189
11190 /*
11191  * Based on:
11192  * Ext JS Library 1.1.1
11193  * Copyright(c) 2006-2007, Ext JS, LLC.
11194  *
11195  * Originally Released Under LGPL - original licence link has changed is not relivant.
11196  *
11197  * Fork - LGPL
11198  * <script type="text/javascript">
11199  */
11200
11201  
11202
11203 /**
11204  * @class Roo.data.Connection
11205  * @extends Roo.util.Observable
11206  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11207  * either to a configured URL, or to a URL specified at request time.<br><br>
11208  * <p>
11209  * Requests made by this class are asynchronous, and will return immediately. No data from
11210  * the server will be available to the statement immediately following the {@link #request} call.
11211  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11212  * <p>
11213  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11214  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11215  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11216  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11217  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11218  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11219  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11220  * standard DOM methods.
11221  * @constructor
11222  * @param {Object} config a configuration object.
11223  */
11224 Roo.data.Connection = function(config){
11225     Roo.apply(this, config);
11226     this.addEvents({
11227         /**
11228          * @event beforerequest
11229          * Fires before a network request is made to retrieve a data object.
11230          * @param {Connection} conn This Connection object.
11231          * @param {Object} options The options config object passed to the {@link #request} method.
11232          */
11233         "beforerequest" : true,
11234         /**
11235          * @event requestcomplete
11236          * Fires if the request was successfully completed.
11237          * @param {Connection} conn This Connection object.
11238          * @param {Object} response The XHR object containing the response data.
11239          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11240          * @param {Object} options The options config object passed to the {@link #request} method.
11241          */
11242         "requestcomplete" : true,
11243         /**
11244          * @event requestexception
11245          * Fires if an error HTTP status was returned from the server.
11246          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11247          * @param {Connection} conn This Connection object.
11248          * @param {Object} response The XHR object containing the response data.
11249          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11250          * @param {Object} options The options config object passed to the {@link #request} method.
11251          */
11252         "requestexception" : true
11253     });
11254     Roo.data.Connection.superclass.constructor.call(this);
11255 };
11256
11257 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11258     /**
11259      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11260      */
11261     /**
11262      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11263      * extra parameters to each request made by this object. (defaults to undefined)
11264      */
11265     /**
11266      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11267      *  to each request made by this object. (defaults to undefined)
11268      */
11269     /**
11270      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11271      */
11272     /**
11273      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11274      */
11275     timeout : 30000,
11276     /**
11277      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11278      * @type Boolean
11279      */
11280     autoAbort:false,
11281
11282     /**
11283      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11284      * @type Boolean
11285      */
11286     disableCaching: true,
11287
11288     /**
11289      * Sends an HTTP request to a remote server.
11290      * @param {Object} options An object which may contain the following properties:<ul>
11291      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11292      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11293      * request, a url encoded string or a function to call to get either.</li>
11294      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11295      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11296      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11297      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11298      * <li>options {Object} The parameter to the request call.</li>
11299      * <li>success {Boolean} True if the request succeeded.</li>
11300      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11301      * </ul></li>
11302      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11303      * The callback is passed the following parameters:<ul>
11304      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11305      * <li>options {Object} The parameter to the request call.</li>
11306      * </ul></li>
11307      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11308      * The callback is passed the following parameters:<ul>
11309      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11310      * <li>options {Object} The parameter to the request call.</li>
11311      * </ul></li>
11312      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11313      * for the callback function. Defaults to the browser window.</li>
11314      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11315      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11316      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11317      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11318      * params for the post data. Any params will be appended to the URL.</li>
11319      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11320      * </ul>
11321      * @return {Number} transactionId
11322      */
11323     request : function(o){
11324         if(this.fireEvent("beforerequest", this, o) !== false){
11325             var p = o.params;
11326
11327             if(typeof p == "function"){
11328                 p = p.call(o.scope||window, o);
11329             }
11330             if(typeof p == "object"){
11331                 p = Roo.urlEncode(o.params);
11332             }
11333             if(this.extraParams){
11334                 var extras = Roo.urlEncode(this.extraParams);
11335                 p = p ? (p + '&' + extras) : extras;
11336             }
11337
11338             var url = o.url || this.url;
11339             if(typeof url == 'function'){
11340                 url = url.call(o.scope||window, o);
11341             }
11342
11343             if(o.form){
11344                 var form = Roo.getDom(o.form);
11345                 url = url || form.action;
11346
11347                 var enctype = form.getAttribute("enctype");
11348                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11349                     return this.doFormUpload(o, p, url);
11350                 }
11351                 var f = Roo.lib.Ajax.serializeForm(form);
11352                 p = p ? (p + '&' + f) : f;
11353             }
11354
11355             var hs = o.headers;
11356             if(this.defaultHeaders){
11357                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11358                 if(!o.headers){
11359                     o.headers = hs;
11360                 }
11361             }
11362
11363             var cb = {
11364                 success: this.handleResponse,
11365                 failure: this.handleFailure,
11366                 scope: this,
11367                 argument: {options: o},
11368                 timeout : this.timeout
11369             };
11370
11371             var method = o.method||this.method||(p ? "POST" : "GET");
11372
11373             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11374                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11375             }
11376
11377             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11378                 if(o.autoAbort){
11379                     this.abort();
11380                 }
11381             }else if(this.autoAbort !== false){
11382                 this.abort();
11383             }
11384
11385             if((method == 'GET' && p) || o.xmlData){
11386                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11387                 p = '';
11388             }
11389             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11390             return this.transId;
11391         }else{
11392             Roo.callback(o.callback, o.scope, [o, null, null]);
11393             return null;
11394         }
11395     },
11396
11397     /**
11398      * Determine whether this object has a request outstanding.
11399      * @param {Number} transactionId (Optional) defaults to the last transaction
11400      * @return {Boolean} True if there is an outstanding request.
11401      */
11402     isLoading : function(transId){
11403         if(transId){
11404             return Roo.lib.Ajax.isCallInProgress(transId);
11405         }else{
11406             return this.transId ? true : false;
11407         }
11408     },
11409
11410     /**
11411      * Aborts any outstanding request.
11412      * @param {Number} transactionId (Optional) defaults to the last transaction
11413      */
11414     abort : function(transId){
11415         if(transId || this.isLoading()){
11416             Roo.lib.Ajax.abort(transId || this.transId);
11417         }
11418     },
11419
11420     // private
11421     handleResponse : function(response){
11422         this.transId = false;
11423         var options = response.argument.options;
11424         response.argument = options ? options.argument : null;
11425         this.fireEvent("requestcomplete", this, response, options);
11426         Roo.callback(options.success, options.scope, [response, options]);
11427         Roo.callback(options.callback, options.scope, [options, true, response]);
11428     },
11429
11430     // private
11431     handleFailure : function(response, e){
11432         this.transId = false;
11433         var options = response.argument.options;
11434         response.argument = options ? options.argument : null;
11435         this.fireEvent("requestexception", this, response, options, e);
11436         Roo.callback(options.failure, options.scope, [response, options]);
11437         Roo.callback(options.callback, options.scope, [options, false, response]);
11438     },
11439
11440     // private
11441     doFormUpload : function(o, ps, url){
11442         var id = Roo.id();
11443         var frame = document.createElement('iframe');
11444         frame.id = id;
11445         frame.name = id;
11446         frame.className = 'x-hidden';
11447         if(Roo.isIE){
11448             frame.src = Roo.SSL_SECURE_URL;
11449         }
11450         document.body.appendChild(frame);
11451
11452         if(Roo.isIE){
11453            document.frames[id].name = id;
11454         }
11455
11456         var form = Roo.getDom(o.form);
11457         form.target = id;
11458         form.method = 'POST';
11459         form.enctype = form.encoding = 'multipart/form-data';
11460         if(url){
11461             form.action = url;
11462         }
11463
11464         var hiddens, hd;
11465         if(ps){ // add dynamic params
11466             hiddens = [];
11467             ps = Roo.urlDecode(ps, false);
11468             for(var k in ps){
11469                 if(ps.hasOwnProperty(k)){
11470                     hd = document.createElement('input');
11471                     hd.type = 'hidden';
11472                     hd.name = k;
11473                     hd.value = ps[k];
11474                     form.appendChild(hd);
11475                     hiddens.push(hd);
11476                 }
11477             }
11478         }
11479
11480         function cb(){
11481             var r = {  // bogus response object
11482                 responseText : '',
11483                 responseXML : null
11484             };
11485
11486             r.argument = o ? o.argument : null;
11487
11488             try { //
11489                 var doc;
11490                 if(Roo.isIE){
11491                     doc = frame.contentWindow.document;
11492                 }else {
11493                     doc = (frame.contentDocument || window.frames[id].document);
11494                 }
11495                 if(doc && doc.body){
11496                     r.responseText = doc.body.innerHTML;
11497                 }
11498                 if(doc && doc.XMLDocument){
11499                     r.responseXML = doc.XMLDocument;
11500                 }else {
11501                     r.responseXML = doc;
11502                 }
11503             }
11504             catch(e) {
11505                 // ignore
11506             }
11507
11508             Roo.EventManager.removeListener(frame, 'load', cb, this);
11509
11510             this.fireEvent("requestcomplete", this, r, o);
11511             Roo.callback(o.success, o.scope, [r, o]);
11512             Roo.callback(o.callback, o.scope, [o, true, r]);
11513
11514             setTimeout(function(){document.body.removeChild(frame);}, 100);
11515         }
11516
11517         Roo.EventManager.on(frame, 'load', cb, this);
11518         form.submit();
11519
11520         if(hiddens){ // remove dynamic params
11521             for(var i = 0, len = hiddens.length; i < len; i++){
11522                 form.removeChild(hiddens[i]);
11523             }
11524         }
11525     }
11526 });
11527
11528 /**
11529  * @class Roo.Ajax
11530  * @extends Roo.data.Connection
11531  * Global Ajax request class.
11532  *
11533  * @singleton
11534  */
11535 Roo.Ajax = new Roo.data.Connection({
11536     // fix up the docs
11537    /**
11538      * @cfg {String} url @hide
11539      */
11540     /**
11541      * @cfg {Object} extraParams @hide
11542      */
11543     /**
11544      * @cfg {Object} defaultHeaders @hide
11545      */
11546     /**
11547      * @cfg {String} method (Optional) @hide
11548      */
11549     /**
11550      * @cfg {Number} timeout (Optional) @hide
11551      */
11552     /**
11553      * @cfg {Boolean} autoAbort (Optional) @hide
11554      */
11555
11556     /**
11557      * @cfg {Boolean} disableCaching (Optional) @hide
11558      */
11559
11560     /**
11561      * @property  disableCaching
11562      * True to add a unique cache-buster param to GET requests. (defaults to true)
11563      * @type Boolean
11564      */
11565     /**
11566      * @property  url
11567      * The default URL to be used for requests to the server. (defaults to undefined)
11568      * @type String
11569      */
11570     /**
11571      * @property  extraParams
11572      * An object containing properties which are used as
11573      * extra parameters to each request made by this object. (defaults to undefined)
11574      * @type Object
11575      */
11576     /**
11577      * @property  defaultHeaders
11578      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11579      * @type Object
11580      */
11581     /**
11582      * @property  method
11583      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11584      * @type String
11585      */
11586     /**
11587      * @property  timeout
11588      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11589      * @type Number
11590      */
11591
11592     /**
11593      * @property  autoAbort
11594      * Whether a new request should abort any pending requests. (defaults to false)
11595      * @type Boolean
11596      */
11597     autoAbort : false,
11598
11599     /**
11600      * Serialize the passed form into a url encoded string
11601      * @param {String/HTMLElement} form
11602      * @return {String}
11603      */
11604     serializeForm : function(form){
11605         return Roo.lib.Ajax.serializeForm(form);
11606     }
11607 });/*
11608  * Based on:
11609  * Ext JS Library 1.1.1
11610  * Copyright(c) 2006-2007, Ext JS, LLC.
11611  *
11612  * Originally Released Under LGPL - original licence link has changed is not relivant.
11613  *
11614  * Fork - LGPL
11615  * <script type="text/javascript">
11616  */
11617  
11618 /**
11619  * Global Ajax request class.
11620  * 
11621  * @class Roo.Ajax
11622  * @extends Roo.data.Connection
11623  * @static
11624  * 
11625  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11626  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11627  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11628  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11629  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11630  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11631  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11632  */
11633 Roo.Ajax = new Roo.data.Connection({
11634     // fix up the docs
11635     /**
11636      * @scope Roo.Ajax
11637      * @type {Boolear} 
11638      */
11639     autoAbort : false,
11640
11641     /**
11642      * Serialize the passed form into a url encoded string
11643      * @scope Roo.Ajax
11644      * @param {String/HTMLElement} form
11645      * @return {String}
11646      */
11647     serializeForm : function(form){
11648         return Roo.lib.Ajax.serializeForm(form);
11649     }
11650 });/*
11651  * Based on:
11652  * Ext JS Library 1.1.1
11653  * Copyright(c) 2006-2007, Ext JS, LLC.
11654  *
11655  * Originally Released Under LGPL - original licence link has changed is not relivant.
11656  *
11657  * Fork - LGPL
11658  * <script type="text/javascript">
11659  */
11660
11661  
11662 /**
11663  * @class Roo.UpdateManager
11664  * @extends Roo.util.Observable
11665  * Provides AJAX-style update for Element object.<br><br>
11666  * Usage:<br>
11667  * <pre><code>
11668  * // Get it from a Roo.Element object
11669  * var el = Roo.get("foo");
11670  * var mgr = el.getUpdateManager();
11671  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11672  * ...
11673  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11674  * <br>
11675  * // or directly (returns the same UpdateManager instance)
11676  * var mgr = new Roo.UpdateManager("myElementId");
11677  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11678  * mgr.on("update", myFcnNeedsToKnow);
11679  * <br>
11680    // short handed call directly from the element object
11681    Roo.get("foo").load({
11682         url: "bar.php",
11683         scripts:true,
11684         params: "for=bar",
11685         text: "Loading Foo..."
11686    });
11687  * </code></pre>
11688  * @constructor
11689  * Create new UpdateManager directly.
11690  * @param {String/HTMLElement/Roo.Element} el The element to update
11691  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11692  */
11693 Roo.UpdateManager = function(el, forceNew){
11694     el = Roo.get(el);
11695     if(!forceNew && el.updateManager){
11696         return el.updateManager;
11697     }
11698     /**
11699      * The Element object
11700      * @type Roo.Element
11701      */
11702     this.el = el;
11703     /**
11704      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11705      * @type String
11706      */
11707     this.defaultUrl = null;
11708
11709     this.addEvents({
11710         /**
11711          * @event beforeupdate
11712          * Fired before an update is made, return false from your handler and the update is cancelled.
11713          * @param {Roo.Element} el
11714          * @param {String/Object/Function} url
11715          * @param {String/Object} params
11716          */
11717         "beforeupdate": true,
11718         /**
11719          * @event update
11720          * Fired after successful update is made.
11721          * @param {Roo.Element} el
11722          * @param {Object} oResponseObject The response Object
11723          */
11724         "update": true,
11725         /**
11726          * @event failure
11727          * Fired on update failure.
11728          * @param {Roo.Element} el
11729          * @param {Object} oResponseObject The response Object
11730          */
11731         "failure": true
11732     });
11733     var d = Roo.UpdateManager.defaults;
11734     /**
11735      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11736      * @type String
11737      */
11738     this.sslBlankUrl = d.sslBlankUrl;
11739     /**
11740      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11741      * @type Boolean
11742      */
11743     this.disableCaching = d.disableCaching;
11744     /**
11745      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11746      * @type String
11747      */
11748     this.indicatorText = d.indicatorText;
11749     /**
11750      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11751      * @type String
11752      */
11753     this.showLoadIndicator = d.showLoadIndicator;
11754     /**
11755      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11756      * @type Number
11757      */
11758     this.timeout = d.timeout;
11759
11760     /**
11761      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11762      * @type Boolean
11763      */
11764     this.loadScripts = d.loadScripts;
11765
11766     /**
11767      * Transaction object of current executing transaction
11768      */
11769     this.transaction = null;
11770
11771     /**
11772      * @private
11773      */
11774     this.autoRefreshProcId = null;
11775     /**
11776      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11777      * @type Function
11778      */
11779     this.refreshDelegate = this.refresh.createDelegate(this);
11780     /**
11781      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11782      * @type Function
11783      */
11784     this.updateDelegate = this.update.createDelegate(this);
11785     /**
11786      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11787      * @type Function
11788      */
11789     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11790     /**
11791      * @private
11792      */
11793     this.successDelegate = this.processSuccess.createDelegate(this);
11794     /**
11795      * @private
11796      */
11797     this.failureDelegate = this.processFailure.createDelegate(this);
11798
11799     if(!this.renderer){
11800      /**
11801       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11802       */
11803     this.renderer = new Roo.UpdateManager.BasicRenderer();
11804     }
11805     
11806     Roo.UpdateManager.superclass.constructor.call(this);
11807 };
11808
11809 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11810     /**
11811      * Get the Element this UpdateManager is bound to
11812      * @return {Roo.Element} The element
11813      */
11814     getEl : function(){
11815         return this.el;
11816     },
11817     /**
11818      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11819      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11820 <pre><code>
11821 um.update({<br/>
11822     url: "your-url.php",<br/>
11823     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11824     callback: yourFunction,<br/>
11825     scope: yourObject, //(optional scope)  <br/>
11826     discardUrl: false, <br/>
11827     nocache: false,<br/>
11828     text: "Loading...",<br/>
11829     timeout: 30,<br/>
11830     scripts: false<br/>
11831 });
11832 </code></pre>
11833      * The only required property is url. The optional properties nocache, text and scripts
11834      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11835      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11836      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11837      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11838      */
11839     update : function(url, params, callback, discardUrl){
11840         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11841             var method = this.method, cfg;
11842             if(typeof url == "object"){ // must be config object
11843                 cfg = url;
11844                 url = cfg.url;
11845                 params = params || cfg.params;
11846                 callback = callback || cfg.callback;
11847                 discardUrl = discardUrl || cfg.discardUrl;
11848                 if(callback && cfg.scope){
11849                     callback = callback.createDelegate(cfg.scope);
11850                 }
11851                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11852                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11853                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11854                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11855                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11856             }
11857             this.showLoading();
11858             if(!discardUrl){
11859                 this.defaultUrl = url;
11860             }
11861             if(typeof url == "function"){
11862                 url = url.call(this);
11863             }
11864
11865             method = method || (params ? "POST" : "GET");
11866             if(method == "GET"){
11867                 url = this.prepareUrl(url);
11868             }
11869
11870             var o = Roo.apply(cfg ||{}, {
11871                 url : url,
11872                 params: params,
11873                 success: this.successDelegate,
11874                 failure: this.failureDelegate,
11875                 callback: undefined,
11876                 timeout: (this.timeout*1000),
11877                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11878             });
11879
11880             this.transaction = Roo.Ajax.request(o);
11881         }
11882     },
11883
11884     /**
11885      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11886      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11887      * @param {String/HTMLElement} form The form Id or form element
11888      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11889      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11890      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11891      */
11892     formUpdate : function(form, url, reset, callback){
11893         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11894             if(typeof url == "function"){
11895                 url = url.call(this);
11896             }
11897             form = Roo.getDom(form);
11898             this.transaction = Roo.Ajax.request({
11899                 form: form,
11900                 url:url,
11901                 success: this.successDelegate,
11902                 failure: this.failureDelegate,
11903                 timeout: (this.timeout*1000),
11904                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11905             });
11906             this.showLoading.defer(1, this);
11907         }
11908     },
11909
11910     /**
11911      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11912      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11913      */
11914     refresh : function(callback){
11915         if(this.defaultUrl == null){
11916             return;
11917         }
11918         this.update(this.defaultUrl, null, callback, true);
11919     },
11920
11921     /**
11922      * Set this element to auto refresh.
11923      * @param {Number} interval How often to update (in seconds).
11924      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11925      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11926      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11927      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11928      */
11929     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11930         if(refreshNow){
11931             this.update(url || this.defaultUrl, params, callback, true);
11932         }
11933         if(this.autoRefreshProcId){
11934             clearInterval(this.autoRefreshProcId);
11935         }
11936         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11937     },
11938
11939     /**
11940      * Stop auto refresh on this element.
11941      */
11942      stopAutoRefresh : function(){
11943         if(this.autoRefreshProcId){
11944             clearInterval(this.autoRefreshProcId);
11945             delete this.autoRefreshProcId;
11946         }
11947     },
11948
11949     isAutoRefreshing : function(){
11950        return this.autoRefreshProcId ? true : false;
11951     },
11952     /**
11953      * Called to update the element to "Loading" state. Override to perform custom action.
11954      */
11955     showLoading : function(){
11956         if(this.showLoadIndicator){
11957             this.el.update(this.indicatorText);
11958         }
11959     },
11960
11961     /**
11962      * Adds unique parameter to query string if disableCaching = true
11963      * @private
11964      */
11965     prepareUrl : function(url){
11966         if(this.disableCaching){
11967             var append = "_dc=" + (new Date().getTime());
11968             if(url.indexOf("?") !== -1){
11969                 url += "&" + append;
11970             }else{
11971                 url += "?" + append;
11972             }
11973         }
11974         return url;
11975     },
11976
11977     /**
11978      * @private
11979      */
11980     processSuccess : function(response){
11981         this.transaction = null;
11982         if(response.argument.form && response.argument.reset){
11983             try{ // put in try/catch since some older FF releases had problems with this
11984                 response.argument.form.reset();
11985             }catch(e){}
11986         }
11987         if(this.loadScripts){
11988             this.renderer.render(this.el, response, this,
11989                 this.updateComplete.createDelegate(this, [response]));
11990         }else{
11991             this.renderer.render(this.el, response, this);
11992             this.updateComplete(response);
11993         }
11994     },
11995
11996     updateComplete : function(response){
11997         this.fireEvent("update", this.el, response);
11998         if(typeof response.argument.callback == "function"){
11999             response.argument.callback(this.el, true, response);
12000         }
12001     },
12002
12003     /**
12004      * @private
12005      */
12006     processFailure : function(response){
12007         this.transaction = null;
12008         this.fireEvent("failure", this.el, response);
12009         if(typeof response.argument.callback == "function"){
12010             response.argument.callback(this.el, false, response);
12011         }
12012     },
12013
12014     /**
12015      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12016      * @param {Object} renderer The object implementing the render() method
12017      */
12018     setRenderer : function(renderer){
12019         this.renderer = renderer;
12020     },
12021
12022     getRenderer : function(){
12023        return this.renderer;
12024     },
12025
12026     /**
12027      * Set the defaultUrl used for updates
12028      * @param {String/Function} defaultUrl The url or a function to call to get the url
12029      */
12030     setDefaultUrl : function(defaultUrl){
12031         this.defaultUrl = defaultUrl;
12032     },
12033
12034     /**
12035      * Aborts the executing transaction
12036      */
12037     abort : function(){
12038         if(this.transaction){
12039             Roo.Ajax.abort(this.transaction);
12040         }
12041     },
12042
12043     /**
12044      * Returns true if an update is in progress
12045      * @return {Boolean}
12046      */
12047     isUpdating : function(){
12048         if(this.transaction){
12049             return Roo.Ajax.isLoading(this.transaction);
12050         }
12051         return false;
12052     }
12053 });
12054
12055 /**
12056  * @class Roo.UpdateManager.defaults
12057  * @static (not really - but it helps the doc tool)
12058  * The defaults collection enables customizing the default properties of UpdateManager
12059  */
12060    Roo.UpdateManager.defaults = {
12061        /**
12062          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12063          * @type Number
12064          */
12065          timeout : 30,
12066
12067          /**
12068          * True to process scripts by default (Defaults to false).
12069          * @type Boolean
12070          */
12071         loadScripts : false,
12072
12073         /**
12074         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12075         * @type String
12076         */
12077         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12078         /**
12079          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12080          * @type Boolean
12081          */
12082         disableCaching : false,
12083         /**
12084          * Whether to show indicatorText when loading (Defaults to true).
12085          * @type Boolean
12086          */
12087         showLoadIndicator : true,
12088         /**
12089          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12090          * @type String
12091          */
12092         indicatorText : '<div class="loading-indicator">Loading...</div>'
12093    };
12094
12095 /**
12096  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12097  *Usage:
12098  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12099  * @param {String/HTMLElement/Roo.Element} el The element to update
12100  * @param {String} url The url
12101  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12102  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12103  * @static
12104  * @deprecated
12105  * @member Roo.UpdateManager
12106  */
12107 Roo.UpdateManager.updateElement = function(el, url, params, options){
12108     var um = Roo.get(el, true).getUpdateManager();
12109     Roo.apply(um, options);
12110     um.update(url, params, options ? options.callback : null);
12111 };
12112 // alias for backwards compat
12113 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12114 /**
12115  * @class Roo.UpdateManager.BasicRenderer
12116  * Default Content renderer. Updates the elements innerHTML with the responseText.
12117  */
12118 Roo.UpdateManager.BasicRenderer = function(){};
12119
12120 Roo.UpdateManager.BasicRenderer.prototype = {
12121     /**
12122      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12123      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12124      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12125      * @param {Roo.Element} el The element being rendered
12126      * @param {Object} response The YUI Connect response object
12127      * @param {UpdateManager} updateManager The calling update manager
12128      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12129      */
12130      render : function(el, response, updateManager, callback){
12131         el.update(response.responseText, updateManager.loadScripts, callback);
12132     }
12133 };
12134 /*
12135  * Based on:
12136  * Ext JS Library 1.1.1
12137  * Copyright(c) 2006-2007, Ext JS, LLC.
12138  *
12139  * Originally Released Under LGPL - original licence link has changed is not relivant.
12140  *
12141  * Fork - LGPL
12142  * <script type="text/javascript">
12143  */
12144
12145 /**
12146  * @class Roo.util.DelayedTask
12147  * Provides a convenient method of performing setTimeout where a new
12148  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12149  * You can use this class to buffer
12150  * the keypress events for a certain number of milliseconds, and perform only if they stop
12151  * for that amount of time.
12152  * @constructor The parameters to this constructor serve as defaults and are not required.
12153  * @param {Function} fn (optional) The default function to timeout
12154  * @param {Object} scope (optional) The default scope of that timeout
12155  * @param {Array} args (optional) The default Array of arguments
12156  */
12157 Roo.util.DelayedTask = function(fn, scope, args){
12158     var id = null, d, t;
12159
12160     var call = function(){
12161         var now = new Date().getTime();
12162         if(now - t >= d){
12163             clearInterval(id);
12164             id = null;
12165             fn.apply(scope, args || []);
12166         }
12167     };
12168     /**
12169      * Cancels any pending timeout and queues a new one
12170      * @param {Number} delay The milliseconds to delay
12171      * @param {Function} newFn (optional) Overrides function passed to constructor
12172      * @param {Object} newScope (optional) Overrides scope passed to constructor
12173      * @param {Array} newArgs (optional) Overrides args passed to constructor
12174      */
12175     this.delay = function(delay, newFn, newScope, newArgs){
12176         if(id && delay != d){
12177             this.cancel();
12178         }
12179         d = delay;
12180         t = new Date().getTime();
12181         fn = newFn || fn;
12182         scope = newScope || scope;
12183         args = newArgs || args;
12184         if(!id){
12185             id = setInterval(call, d);
12186         }
12187     };
12188
12189     /**
12190      * Cancel the last queued timeout
12191      */
12192     this.cancel = function(){
12193         if(id){
12194             clearInterval(id);
12195             id = null;
12196         }
12197     };
12198 };/*
12199  * Based on:
12200  * Ext JS Library 1.1.1
12201  * Copyright(c) 2006-2007, Ext JS, LLC.
12202  *
12203  * Originally Released Under LGPL - original licence link has changed is not relivant.
12204  *
12205  * Fork - LGPL
12206  * <script type="text/javascript">
12207  */
12208  
12209  
12210 Roo.util.TaskRunner = function(interval){
12211     interval = interval || 10;
12212     var tasks = [], removeQueue = [];
12213     var id = 0;
12214     var running = false;
12215
12216     var stopThread = function(){
12217         running = false;
12218         clearInterval(id);
12219         id = 0;
12220     };
12221
12222     var startThread = function(){
12223         if(!running){
12224             running = true;
12225             id = setInterval(runTasks, interval);
12226         }
12227     };
12228
12229     var removeTask = function(task){
12230         removeQueue.push(task);
12231         if(task.onStop){
12232             task.onStop();
12233         }
12234     };
12235
12236     var runTasks = function(){
12237         if(removeQueue.length > 0){
12238             for(var i = 0, len = removeQueue.length; i < len; i++){
12239                 tasks.remove(removeQueue[i]);
12240             }
12241             removeQueue = [];
12242             if(tasks.length < 1){
12243                 stopThread();
12244                 return;
12245             }
12246         }
12247         var now = new Date().getTime();
12248         for(var i = 0, len = tasks.length; i < len; ++i){
12249             var t = tasks[i];
12250             var itime = now - t.taskRunTime;
12251             if(t.interval <= itime){
12252                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12253                 t.taskRunTime = now;
12254                 if(rt === false || t.taskRunCount === t.repeat){
12255                     removeTask(t);
12256                     return;
12257                 }
12258             }
12259             if(t.duration && t.duration <= (now - t.taskStartTime)){
12260                 removeTask(t);
12261             }
12262         }
12263     };
12264
12265     /**
12266      * Queues a new task.
12267      * @param {Object} task
12268      */
12269     this.start = function(task){
12270         tasks.push(task);
12271         task.taskStartTime = new Date().getTime();
12272         task.taskRunTime = 0;
12273         task.taskRunCount = 0;
12274         startThread();
12275         return task;
12276     };
12277
12278     this.stop = function(task){
12279         removeTask(task);
12280         return task;
12281     };
12282
12283     this.stopAll = function(){
12284         stopThread();
12285         for(var i = 0, len = tasks.length; i < len; i++){
12286             if(tasks[i].onStop){
12287                 tasks[i].onStop();
12288             }
12289         }
12290         tasks = [];
12291         removeQueue = [];
12292     };
12293 };
12294
12295 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12296  * Based on:
12297  * Ext JS Library 1.1.1
12298  * Copyright(c) 2006-2007, Ext JS, LLC.
12299  *
12300  * Originally Released Under LGPL - original licence link has changed is not relivant.
12301  *
12302  * Fork - LGPL
12303  * <script type="text/javascript">
12304  */
12305
12306  
12307 /**
12308  * @class Roo.util.MixedCollection
12309  * @extends Roo.util.Observable
12310  * A Collection class that maintains both numeric indexes and keys and exposes events.
12311  * @constructor
12312  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12313  * collection (defaults to false)
12314  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12315  * and return the key value for that item.  This is used when available to look up the key on items that
12316  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12317  * equivalent to providing an implementation for the {@link #getKey} method.
12318  */
12319 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12320     this.items = [];
12321     this.map = {};
12322     this.keys = [];
12323     this.length = 0;
12324     this.addEvents({
12325         /**
12326          * @event clear
12327          * Fires when the collection is cleared.
12328          */
12329         "clear" : true,
12330         /**
12331          * @event add
12332          * Fires when an item is added to the collection.
12333          * @param {Number} index The index at which the item was added.
12334          * @param {Object} o The item added.
12335          * @param {String} key The key associated with the added item.
12336          */
12337         "add" : true,
12338         /**
12339          * @event replace
12340          * Fires when an item is replaced in the collection.
12341          * @param {String} key he key associated with the new added.
12342          * @param {Object} old The item being replaced.
12343          * @param {Object} new The new item.
12344          */
12345         "replace" : true,
12346         /**
12347          * @event remove
12348          * Fires when an item is removed from the collection.
12349          * @param {Object} o The item being removed.
12350          * @param {String} key (optional) The key associated with the removed item.
12351          */
12352         "remove" : true,
12353         "sort" : true
12354     });
12355     this.allowFunctions = allowFunctions === true;
12356     if(keyFn){
12357         this.getKey = keyFn;
12358     }
12359     Roo.util.MixedCollection.superclass.constructor.call(this);
12360 };
12361
12362 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12363     allowFunctions : false,
12364     
12365 /**
12366  * Adds an item to the collection.
12367  * @param {String} key The key to associate with the item
12368  * @param {Object} o The item to add.
12369  * @return {Object} The item added.
12370  */
12371     add : function(key, o){
12372         if(arguments.length == 1){
12373             o = arguments[0];
12374             key = this.getKey(o);
12375         }
12376         if(typeof key == "undefined" || key === null){
12377             this.length++;
12378             this.items.push(o);
12379             this.keys.push(null);
12380         }else{
12381             var old = this.map[key];
12382             if(old){
12383                 return this.replace(key, o);
12384             }
12385             this.length++;
12386             this.items.push(o);
12387             this.map[key] = o;
12388             this.keys.push(key);
12389         }
12390         this.fireEvent("add", this.length-1, o, key);
12391         return o;
12392     },
12393        
12394 /**
12395   * MixedCollection has a generic way to fetch keys if you implement getKey.
12396 <pre><code>
12397 // normal way
12398 var mc = new Roo.util.MixedCollection();
12399 mc.add(someEl.dom.id, someEl);
12400 mc.add(otherEl.dom.id, otherEl);
12401 //and so on
12402
12403 // using getKey
12404 var mc = new Roo.util.MixedCollection();
12405 mc.getKey = function(el){
12406    return el.dom.id;
12407 };
12408 mc.add(someEl);
12409 mc.add(otherEl);
12410
12411 // or via the constructor
12412 var mc = new Roo.util.MixedCollection(false, function(el){
12413    return el.dom.id;
12414 });
12415 mc.add(someEl);
12416 mc.add(otherEl);
12417 </code></pre>
12418  * @param o {Object} The item for which to find the key.
12419  * @return {Object} The key for the passed item.
12420  */
12421     getKey : function(o){
12422          return o.id; 
12423     },
12424    
12425 /**
12426  * Replaces an item in the collection.
12427  * @param {String} key The key associated with the item to replace, or the item to replace.
12428  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12429  * @return {Object}  The new item.
12430  */
12431     replace : function(key, o){
12432         if(arguments.length == 1){
12433             o = arguments[0];
12434             key = this.getKey(o);
12435         }
12436         var old = this.item(key);
12437         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12438              return this.add(key, o);
12439         }
12440         var index = this.indexOfKey(key);
12441         this.items[index] = o;
12442         this.map[key] = o;
12443         this.fireEvent("replace", key, old, o);
12444         return o;
12445     },
12446    
12447 /**
12448  * Adds all elements of an Array or an Object to the collection.
12449  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12450  * an Array of values, each of which are added to the collection.
12451  */
12452     addAll : function(objs){
12453         if(arguments.length > 1 || objs instanceof Array){
12454             var args = arguments.length > 1 ? arguments : objs;
12455             for(var i = 0, len = args.length; i < len; i++){
12456                 this.add(args[i]);
12457             }
12458         }else{
12459             for(var key in objs){
12460                 if(this.allowFunctions || typeof objs[key] != "function"){
12461                     this.add(key, objs[key]);
12462                 }
12463             }
12464         }
12465     },
12466    
12467 /**
12468  * Executes the specified function once for every item in the collection, passing each
12469  * item as the first and only parameter. returning false from the function will stop the iteration.
12470  * @param {Function} fn The function to execute for each item.
12471  * @param {Object} scope (optional) The scope in which to execute the function.
12472  */
12473     each : function(fn, scope){
12474         var items = [].concat(this.items); // each safe for removal
12475         for(var i = 0, len = items.length; i < len; i++){
12476             if(fn.call(scope || items[i], items[i], i, len) === false){
12477                 break;
12478             }
12479         }
12480     },
12481    
12482 /**
12483  * Executes the specified function once for every key in the collection, passing each
12484  * key, and its associated item as the first two parameters.
12485  * @param {Function} fn The function to execute for each item.
12486  * @param {Object} scope (optional) The scope in which to execute the function.
12487  */
12488     eachKey : function(fn, scope){
12489         for(var i = 0, len = this.keys.length; i < len; i++){
12490             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12491         }
12492     },
12493    
12494 /**
12495  * Returns the first item in the collection which elicits a true return value from the
12496  * passed selection function.
12497  * @param {Function} fn The selection function to execute for each item.
12498  * @param {Object} scope (optional) The scope in which to execute the function.
12499  * @return {Object} The first item in the collection which returned true from the selection function.
12500  */
12501     find : function(fn, scope){
12502         for(var i = 0, len = this.items.length; i < len; i++){
12503             if(fn.call(scope || window, this.items[i], this.keys[i])){
12504                 return this.items[i];
12505             }
12506         }
12507         return null;
12508     },
12509    
12510 /**
12511  * Inserts an item at the specified index in the collection.
12512  * @param {Number} index The index to insert the item at.
12513  * @param {String} key The key to associate with the new item, or the item itself.
12514  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12515  * @return {Object} The item inserted.
12516  */
12517     insert : function(index, key, o){
12518         if(arguments.length == 2){
12519             o = arguments[1];
12520             key = this.getKey(o);
12521         }
12522         if(index >= this.length){
12523             return this.add(key, o);
12524         }
12525         this.length++;
12526         this.items.splice(index, 0, o);
12527         if(typeof key != "undefined" && key != null){
12528             this.map[key] = o;
12529         }
12530         this.keys.splice(index, 0, key);
12531         this.fireEvent("add", index, o, key);
12532         return o;
12533     },
12534    
12535 /**
12536  * Removed an item from the collection.
12537  * @param {Object} o The item to remove.
12538  * @return {Object} The item removed.
12539  */
12540     remove : function(o){
12541         return this.removeAt(this.indexOf(o));
12542     },
12543    
12544 /**
12545  * Remove an item from a specified index in the collection.
12546  * @param {Number} index The index within the collection of the item to remove.
12547  */
12548     removeAt : function(index){
12549         if(index < this.length && index >= 0){
12550             this.length--;
12551             var o = this.items[index];
12552             this.items.splice(index, 1);
12553             var key = this.keys[index];
12554             if(typeof key != "undefined"){
12555                 delete this.map[key];
12556             }
12557             this.keys.splice(index, 1);
12558             this.fireEvent("remove", o, key);
12559         }
12560     },
12561    
12562 /**
12563  * Removed an item associated with the passed key fom the collection.
12564  * @param {String} key The key of the item to remove.
12565  */
12566     removeKey : function(key){
12567         return this.removeAt(this.indexOfKey(key));
12568     },
12569    
12570 /**
12571  * Returns the number of items in the collection.
12572  * @return {Number} the number of items in the collection.
12573  */
12574     getCount : function(){
12575         return this.length; 
12576     },
12577    
12578 /**
12579  * Returns index within the collection of the passed Object.
12580  * @param {Object} o The item to find the index of.
12581  * @return {Number} index of the item.
12582  */
12583     indexOf : function(o){
12584         if(!this.items.indexOf){
12585             for(var i = 0, len = this.items.length; i < len; i++){
12586                 if(this.items[i] == o) return i;
12587             }
12588             return -1;
12589         }else{
12590             return this.items.indexOf(o);
12591         }
12592     },
12593    
12594 /**
12595  * Returns index within the collection of the passed key.
12596  * @param {String} key The key to find the index of.
12597  * @return {Number} index of the key.
12598  */
12599     indexOfKey : function(key){
12600         if(!this.keys.indexOf){
12601             for(var i = 0, len = this.keys.length; i < len; i++){
12602                 if(this.keys[i] == key) return i;
12603             }
12604             return -1;
12605         }else{
12606             return this.keys.indexOf(key);
12607         }
12608     },
12609    
12610 /**
12611  * Returns the item associated with the passed key OR index. Key has priority over index.
12612  * @param {String/Number} key The key or index of the item.
12613  * @return {Object} The item associated with the passed key.
12614  */
12615     item : function(key){
12616         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12617         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12618     },
12619     
12620 /**
12621  * Returns the item at the specified index.
12622  * @param {Number} index The index of the item.
12623  * @return {Object}
12624  */
12625     itemAt : function(index){
12626         return this.items[index];
12627     },
12628     
12629 /**
12630  * Returns the item associated with the passed key.
12631  * @param {String/Number} key The key of the item.
12632  * @return {Object} The item associated with the passed key.
12633  */
12634     key : function(key){
12635         return this.map[key];
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as an item.
12640  * @param {Object} o  The Object to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as an item.
12642  */
12643     contains : function(o){
12644         return this.indexOf(o) != -1;
12645     },
12646    
12647 /**
12648  * Returns true if the collection contains the passed Object as a key.
12649  * @param {String} key The key to look for in the collection.
12650  * @return {Boolean} True if the collection contains the Object as a key.
12651  */
12652     containsKey : function(key){
12653         return typeof this.map[key] != "undefined";
12654     },
12655    
12656 /**
12657  * Removes all items from the collection.
12658  */
12659     clear : function(){
12660         this.length = 0;
12661         this.items = [];
12662         this.keys = [];
12663         this.map = {};
12664         this.fireEvent("clear");
12665     },
12666    
12667 /**
12668  * Returns the first item in the collection.
12669  * @return {Object} the first item in the collection..
12670  */
12671     first : function(){
12672         return this.items[0]; 
12673     },
12674    
12675 /**
12676  * Returns the last item in the collection.
12677  * @return {Object} the last item in the collection..
12678  */
12679     last : function(){
12680         return this.items[this.length-1];   
12681     },
12682     
12683     _sort : function(property, dir, fn){
12684         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12685         fn = fn || function(a, b){
12686             return a-b;
12687         };
12688         var c = [], k = this.keys, items = this.items;
12689         for(var i = 0, len = items.length; i < len; i++){
12690             c[c.length] = {key: k[i], value: items[i], index: i};
12691         }
12692         c.sort(function(a, b){
12693             var v = fn(a[property], b[property]) * dsc;
12694             if(v == 0){
12695                 v = (a.index < b.index ? -1 : 1);
12696             }
12697             return v;
12698         });
12699         for(var i = 0, len = c.length; i < len; i++){
12700             items[i] = c[i].value;
12701             k[i] = c[i].key;
12702         }
12703         this.fireEvent("sort", this);
12704     },
12705     
12706     /**
12707      * Sorts this collection with the passed comparison function
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) comparison function
12710      */
12711     sort : function(dir, fn){
12712         this._sort("value", dir, fn);
12713     },
12714     
12715     /**
12716      * Sorts this collection by keys
12717      * @param {String} direction (optional) "ASC" or "DESC"
12718      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12719      */
12720     keySort : function(dir, fn){
12721         this._sort("key", dir, fn || function(a, b){
12722             return String(a).toUpperCase()-String(b).toUpperCase();
12723         });
12724     },
12725     
12726     /**
12727      * Returns a range of items in this collection
12728      * @param {Number} startIndex (optional) defaults to 0
12729      * @param {Number} endIndex (optional) default to the last item
12730      * @return {Array} An array of items
12731      */
12732     getRange : function(start, end){
12733         var items = this.items;
12734         if(items.length < 1){
12735             return [];
12736         }
12737         start = start || 0;
12738         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12739         var r = [];
12740         if(start <= end){
12741             for(var i = start; i <= end; i++) {
12742                     r[r.length] = items[i];
12743             }
12744         }else{
12745             for(var i = start; i >= end; i--) {
12746                     r[r.length] = items[i];
12747             }
12748         }
12749         return r;
12750     },
12751         
12752     /**
12753      * Filter the <i>objects</i> in this collection by a specific property. 
12754      * Returns a new collection that has been filtered.
12755      * @param {String} property A property on your objects
12756      * @param {String/RegExp} value Either string that the property values 
12757      * should start with or a RegExp to test against the property
12758      * @return {MixedCollection} The new filtered collection
12759      */
12760     filter : function(property, value){
12761         if(!value.exec){ // not a regex
12762             value = String(value);
12763             if(value.length == 0){
12764                 return this.clone();
12765             }
12766             value = new RegExp("^" + Roo.escapeRe(value), "i");
12767         }
12768         return this.filterBy(function(o){
12769             return o && value.test(o[property]);
12770         });
12771         },
12772     
12773     /**
12774      * Filter by a function. * Returns a new collection that has been filtered.
12775      * The passed function will be called with each 
12776      * object in the collection. If the function returns true, the value is included 
12777      * otherwise it is filtered.
12778      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12779      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12780      * @return {MixedCollection} The new filtered collection
12781      */
12782     filterBy : function(fn, scope){
12783         var r = new Roo.util.MixedCollection();
12784         r.getKey = this.getKey;
12785         var k = this.keys, it = this.items;
12786         for(var i = 0, len = it.length; i < len; i++){
12787             if(fn.call(scope||this, it[i], k[i])){
12788                                 r.add(k[i], it[i]);
12789                         }
12790         }
12791         return r;
12792     },
12793     
12794     /**
12795      * Creates a duplicate of this collection
12796      * @return {MixedCollection}
12797      */
12798     clone : function(){
12799         var r = new Roo.util.MixedCollection();
12800         var k = this.keys, it = this.items;
12801         for(var i = 0, len = it.length; i < len; i++){
12802             r.add(k[i], it[i]);
12803         }
12804         r.getKey = this.getKey;
12805         return r;
12806     }
12807 });
12808 /**
12809  * Returns the item associated with the passed key or index.
12810  * @method
12811  * @param {String/Number} key The key or index of the item.
12812  * @return {Object} The item associated with the passed key.
12813  */
12814 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12815  * Based on:
12816  * Ext JS Library 1.1.1
12817  * Copyright(c) 2006-2007, Ext JS, LLC.
12818  *
12819  * Originally Released Under LGPL - original licence link has changed is not relivant.
12820  *
12821  * Fork - LGPL
12822  * <script type="text/javascript">
12823  */
12824 /**
12825  * @class Roo.util.JSON
12826  * Modified version of Douglas Crockford"s json.js that doesn"t
12827  * mess with the Object prototype 
12828  * http://www.json.org/js.html
12829  * @singleton
12830  */
12831 Roo.util.JSON = new (function(){
12832     var useHasOwn = {}.hasOwnProperty ? true : false;
12833     
12834     // crashes Safari in some instances
12835     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12836     
12837     var pad = function(n) {
12838         return n < 10 ? "0" + n : n;
12839     };
12840     
12841     var m = {
12842         "\b": '\\b',
12843         "\t": '\\t',
12844         "\n": '\\n',
12845         "\f": '\\f',
12846         "\r": '\\r',
12847         '"' : '\\"',
12848         "\\": '\\\\'
12849     };
12850
12851     var encodeString = function(s){
12852         if (/["\\\x00-\x1f]/.test(s)) {
12853             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12854                 var c = m[b];
12855                 if(c){
12856                     return c;
12857                 }
12858                 c = b.charCodeAt();
12859                 return "\\u00" +
12860                     Math.floor(c / 16).toString(16) +
12861                     (c % 16).toString(16);
12862             }) + '"';
12863         }
12864         return '"' + s + '"';
12865     };
12866     
12867     var encodeArray = function(o){
12868         var a = ["["], b, i, l = o.length, v;
12869             for (i = 0; i < l; i += 1) {
12870                 v = o[i];
12871                 switch (typeof v) {
12872                     case "undefined":
12873                     case "function":
12874                     case "unknown":
12875                         break;
12876                     default:
12877                         if (b) {
12878                             a.push(',');
12879                         }
12880                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12881                         b = true;
12882                 }
12883             }
12884             a.push("]");
12885             return a.join("");
12886     };
12887     
12888     var encodeDate = function(o){
12889         return '"' + o.getFullYear() + "-" +
12890                 pad(o.getMonth() + 1) + "-" +
12891                 pad(o.getDate()) + "T" +
12892                 pad(o.getHours()) + ":" +
12893                 pad(o.getMinutes()) + ":" +
12894                 pad(o.getSeconds()) + '"';
12895     };
12896     
12897     /**
12898      * Encodes an Object, Array or other value
12899      * @param {Mixed} o The variable to encode
12900      * @return {String} The JSON string
12901      */
12902     this.encode = function(o)
12903     {
12904         // should this be extended to fully wrap stringify..
12905         
12906         if(typeof o == "undefined" || o === null){
12907             return "null";
12908         }else if(o instanceof Array){
12909             return encodeArray(o);
12910         }else if(o instanceof Date){
12911             return encodeDate(o);
12912         }else if(typeof o == "string"){
12913             return encodeString(o);
12914         }else if(typeof o == "number"){
12915             return isFinite(o) ? String(o) : "null";
12916         }else if(typeof o == "boolean"){
12917             return String(o);
12918         }else {
12919             var a = ["{"], b, i, v;
12920             for (i in o) {
12921                 if(!useHasOwn || o.hasOwnProperty(i)) {
12922                     v = o[i];
12923                     switch (typeof v) {
12924                     case "undefined":
12925                     case "function":
12926                     case "unknown":
12927                         break;
12928                     default:
12929                         if(b){
12930                             a.push(',');
12931                         }
12932                         a.push(this.encode(i), ":",
12933                                 v === null ? "null" : this.encode(v));
12934                         b = true;
12935                     }
12936                 }
12937             }
12938             a.push("}");
12939             return a.join("");
12940         }
12941     };
12942     
12943     /**
12944      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12945      * @param {String} json The JSON string
12946      * @return {Object} The resulting object
12947      */
12948     this.decode = function(json){
12949         
12950         return  /** eval:var:json */ eval("(" + json + ')');
12951     };
12952 })();
12953 /** 
12954  * Shorthand for {@link Roo.util.JSON#encode}
12955  * @member Roo encode 
12956  * @method */
12957 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12958 /** 
12959  * Shorthand for {@link Roo.util.JSON#decode}
12960  * @member Roo decode 
12961  * @method */
12962 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12963 /*
12964  * Based on:
12965  * Ext JS Library 1.1.1
12966  * Copyright(c) 2006-2007, Ext JS, LLC.
12967  *
12968  * Originally Released Under LGPL - original licence link has changed is not relivant.
12969  *
12970  * Fork - LGPL
12971  * <script type="text/javascript">
12972  */
12973  
12974 /**
12975  * @class Roo.util.Format
12976  * Reusable data formatting functions
12977  * @singleton
12978  */
12979 Roo.util.Format = function(){
12980     var trimRe = /^\s+|\s+$/g;
12981     return {
12982         /**
12983          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12984          * @param {String} value The string to truncate
12985          * @param {Number} length The maximum length to allow before truncating
12986          * @return {String} The converted text
12987          */
12988         ellipsis : function(value, len){
12989             if(value && value.length > len){
12990                 return value.substr(0, len-3)+"...";
12991             }
12992             return value;
12993         },
12994
12995         /**
12996          * Checks a reference and converts it to empty string if it is undefined
12997          * @param {Mixed} value Reference to check
12998          * @return {Mixed} Empty string if converted, otherwise the original value
12999          */
13000         undef : function(value){
13001             return typeof value != "undefined" ? value : "";
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13006          * @param {String} value The string to encode
13007          * @return {String} The encoded text
13008          */
13009         htmlEncode : function(value){
13010             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13011         },
13012
13013         /**
13014          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13015          * @param {String} value The string to decode
13016          * @return {String} The decoded text
13017          */
13018         htmlDecode : function(value){
13019             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13020         },
13021
13022         /**
13023          * Trims any whitespace from either side of a string
13024          * @param {String} value The text to trim
13025          * @return {String} The trimmed text
13026          */
13027         trim : function(value){
13028             return String(value).replace(trimRe, "");
13029         },
13030
13031         /**
13032          * Returns a substring from within an original string
13033          * @param {String} value The original text
13034          * @param {Number} start The start index of the substring
13035          * @param {Number} length The length of the substring
13036          * @return {String} The substring
13037          */
13038         substr : function(value, start, length){
13039             return String(value).substr(start, length);
13040         },
13041
13042         /**
13043          * Converts a string to all lower case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         lowercase : function(value){
13048             return String(value).toLowerCase();
13049         },
13050
13051         /**
13052          * Converts a string to all upper case letters
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         uppercase : function(value){
13057             return String(value).toUpperCase();
13058         },
13059
13060         /**
13061          * Converts the first character only of a string to upper case
13062          * @param {String} value The text to convert
13063          * @return {String} The converted text
13064          */
13065         capitalize : function(value){
13066             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13067         },
13068
13069         // private
13070         call : function(value, fn){
13071             if(arguments.length > 2){
13072                 var args = Array.prototype.slice.call(arguments, 2);
13073                 args.unshift(value);
13074                  
13075                 return /** eval:var:value */  eval(fn).apply(window, args);
13076             }else{
13077                 /** eval:var:value */
13078                 return /** eval:var:value */ eval(fn).call(window, value);
13079             }
13080         },
13081
13082        
13083         /**
13084          * safer version of Math.toFixed..??/
13085          * @param {Number/String} value The numeric value to format
13086          * @param {Number/String} value Decimal places 
13087          * @return {String} The formatted currency string
13088          */
13089         toFixed : function(v, n)
13090         {
13091             // why not use to fixed - precision is buggered???
13092             if (!n) {
13093                 return Math.round(v-0);
13094             }
13095             var fact = Math.pow(10,n+1);
13096             v = (Math.round((v-0)*fact))/fact;
13097             var z = (''+fact).substring(2);
13098             if (v == Math.floor(v)) {
13099                 return Math.floor(v) + '.' + z;
13100             }
13101             
13102             // now just padd decimals..
13103             var ps = String(v).split('.');
13104             var fd = (ps[1] + z);
13105             var r = fd.substring(0,n); 
13106             var rm = fd.substring(n); 
13107             if (rm < 5) {
13108                 return ps[0] + '.' + r;
13109             }
13110             r*=1; // turn it into a number;
13111             r++;
13112             if (String(r).length != n) {
13113                 ps[0]*=1;
13114                 ps[0]++;
13115                 r = String(r).substring(1); // chop the end off.
13116             }
13117             
13118             return ps[0] + '.' + r;
13119              
13120         },
13121         
13122         /**
13123          * Format a number as US currency
13124          * @param {Number/String} value The numeric value to format
13125          * @return {String} The formatted currency string
13126          */
13127         usMoney : function(v){
13128             v = (Math.round((v-0)*100))/100;
13129             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13130             v = String(v);
13131             var ps = v.split('.');
13132             var whole = ps[0];
13133             var sub = ps[1] ? '.'+ ps[1] : '.00';
13134             var r = /(\d+)(\d{3})/;
13135             while (r.test(whole)) {
13136                 whole = whole.replace(r, '$1' + ',' + '$2');
13137             }
13138             return "$" + whole + sub ;
13139         },
13140         
13141         /**
13142          * Parse a value into a formatted date using the specified format pattern.
13143          * @param {Mixed} value The value to format
13144          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13145          * @return {String} The formatted date string
13146          */
13147         date : function(v, format){
13148             if(!v){
13149                 return "";
13150             }
13151             if(!(v instanceof Date)){
13152                 v = new Date(Date.parse(v));
13153             }
13154             return v.dateFormat(format || "m/d/Y");
13155         },
13156
13157         /**
13158          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13159          * @param {String} format Any valid date format string
13160          * @return {Function} The date formatting function
13161          */
13162         dateRenderer : function(format){
13163             return function(v){
13164                 return Roo.util.Format.date(v, format);  
13165             };
13166         },
13167
13168         // private
13169         stripTagsRE : /<\/?[^>]+>/gi,
13170         
13171         /**
13172          * Strips all HTML tags
13173          * @param {Mixed} value The text from which to strip tags
13174          * @return {String} The stripped text
13175          */
13176         stripTags : function(v){
13177             return !v ? v : String(v).replace(this.stripTagsRE, "");
13178         }
13179     };
13180 }();/*
13181  * Based on:
13182  * Ext JS Library 1.1.1
13183  * Copyright(c) 2006-2007, Ext JS, LLC.
13184  *
13185  * Originally Released Under LGPL - original licence link has changed is not relivant.
13186  *
13187  * Fork - LGPL
13188  * <script type="text/javascript">
13189  */
13190
13191
13192  
13193
13194 /**
13195  * @class Roo.MasterTemplate
13196  * @extends Roo.Template
13197  * Provides a template that can have child templates. The syntax is:
13198 <pre><code>
13199 var t = new Roo.MasterTemplate(
13200         '&lt;select name="{name}"&gt;',
13201                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13202         '&lt;/select&gt;'
13203 );
13204 t.add('options', {value: 'foo', text: 'bar'});
13205 // or you can add multiple child elements in one shot
13206 t.addAll('options', [
13207     {value: 'foo', text: 'bar'},
13208     {value: 'foo2', text: 'bar2'},
13209     {value: 'foo3', text: 'bar3'}
13210 ]);
13211 // then append, applying the master template values
13212 t.append('my-form', {name: 'my-select'});
13213 </code></pre>
13214 * A name attribute for the child template is not required if you have only one child
13215 * template or you want to refer to them by index.
13216  */
13217 Roo.MasterTemplate = function(){
13218     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13219     this.originalHtml = this.html;
13220     var st = {};
13221     var m, re = this.subTemplateRe;
13222     re.lastIndex = 0;
13223     var subIndex = 0;
13224     while(m = re.exec(this.html)){
13225         var name = m[1], content = m[2];
13226         st[subIndex] = {
13227             name: name,
13228             index: subIndex,
13229             buffer: [],
13230             tpl : new Roo.Template(content)
13231         };
13232         if(name){
13233             st[name] = st[subIndex];
13234         }
13235         st[subIndex].tpl.compile();
13236         st[subIndex].tpl.call = this.call.createDelegate(this);
13237         subIndex++;
13238     }
13239     this.subCount = subIndex;
13240     this.subs = st;
13241 };
13242 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13243     /**
13244     * The regular expression used to match sub templates
13245     * @type RegExp
13246     * @property
13247     */
13248     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13249
13250     /**
13251      * Applies the passed values to a child template.
13252      * @param {String/Number} name (optional) The name or index of the child template
13253      * @param {Array/Object} values The values to be applied to the template
13254      * @return {MasterTemplate} this
13255      */
13256      add : function(name, values){
13257         if(arguments.length == 1){
13258             values = arguments[0];
13259             name = 0;
13260         }
13261         var s = this.subs[name];
13262         s.buffer[s.buffer.length] = s.tpl.apply(values);
13263         return this;
13264     },
13265
13266     /**
13267      * Applies all the passed values to a child template.
13268      * @param {String/Number} name (optional) The name or index of the child template
13269      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13270      * @param {Boolean} reset (optional) True to reset the template first
13271      * @return {MasterTemplate} this
13272      */
13273     fill : function(name, values, reset){
13274         var a = arguments;
13275         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13276             values = a[0];
13277             name = 0;
13278             reset = a[1];
13279         }
13280         if(reset){
13281             this.reset();
13282         }
13283         for(var i = 0, len = values.length; i < len; i++){
13284             this.add(name, values[i]);
13285         }
13286         return this;
13287     },
13288
13289     /**
13290      * Resets the template for reuse
13291      * @return {MasterTemplate} this
13292      */
13293      reset : function(){
13294         var s = this.subs;
13295         for(var i = 0; i < this.subCount; i++){
13296             s[i].buffer = [];
13297         }
13298         return this;
13299     },
13300
13301     applyTemplate : function(values){
13302         var s = this.subs;
13303         var replaceIndex = -1;
13304         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13305             return s[++replaceIndex].buffer.join("");
13306         });
13307         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13308     },
13309
13310     apply : function(){
13311         return this.applyTemplate.apply(this, arguments);
13312     },
13313
13314     compile : function(){return this;}
13315 });
13316
13317 /**
13318  * Alias for fill().
13319  * @method
13320  */
13321 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13322  /**
13323  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13324  * var tpl = Roo.MasterTemplate.from('element-id');
13325  * @param {String/HTMLElement} el
13326  * @param {Object} config
13327  * @static
13328  */
13329 Roo.MasterTemplate.from = function(el, config){
13330     el = Roo.getDom(el);
13331     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13332 };/*
13333  * Based on:
13334  * Ext JS Library 1.1.1
13335  * Copyright(c) 2006-2007, Ext JS, LLC.
13336  *
13337  * Originally Released Under LGPL - original licence link has changed is not relivant.
13338  *
13339  * Fork - LGPL
13340  * <script type="text/javascript">
13341  */
13342
13343  
13344 /**
13345  * @class Roo.util.CSS
13346  * Utility class for manipulating CSS rules
13347  * @singleton
13348  */
13349 Roo.util.CSS = function(){
13350         var rules = null;
13351         var doc = document;
13352
13353     var camelRe = /(-[a-z])/gi;
13354     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13355
13356    return {
13357    /**
13358     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13359     * tag and appended to the HEAD of the document.
13360     * @param {String|Object} cssText The text containing the css rules
13361     * @param {String} id An id to add to the stylesheet for later removal
13362     * @return {StyleSheet}
13363     */
13364     createStyleSheet : function(cssText, id){
13365         var ss;
13366         var head = doc.getElementsByTagName("head")[0];
13367         var nrules = doc.createElement("style");
13368         nrules.setAttribute("type", "text/css");
13369         if(id){
13370             nrules.setAttribute("id", id);
13371         }
13372         if (typeof(cssText) != 'string') {
13373             // support object maps..
13374             // not sure if this a good idea.. 
13375             // perhaps it should be merged with the general css handling
13376             // and handle js style props.
13377             var cssTextNew = [];
13378             for(var n in cssText) {
13379                 var citems = [];
13380                 for(var k in cssText[n]) {
13381                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13382                 }
13383                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13384                 
13385             }
13386             cssText = cssTextNew.join("\n");
13387             
13388         }
13389        
13390        
13391        if(Roo.isIE){
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet;
13394            ss.cssText = cssText;
13395        }else{
13396            try{
13397                 nrules.appendChild(doc.createTextNode(cssText));
13398            }catch(e){
13399                nrules.cssText = cssText; 
13400            }
13401            head.appendChild(nrules);
13402            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13403        }
13404        this.cacheStyleSheet(ss);
13405        return ss;
13406    },
13407
13408    /**
13409     * Removes a style or link tag by id
13410     * @param {String} id The id of the tag
13411     */
13412    removeStyleSheet : function(id){
13413        var existing = doc.getElementById(id);
13414        if(existing){
13415            existing.parentNode.removeChild(existing);
13416        }
13417    },
13418
13419    /**
13420     * Dynamically swaps an existing stylesheet reference for a new one
13421     * @param {String} id The id of an existing link tag to remove
13422     * @param {String} url The href of the new stylesheet to include
13423     */
13424    swapStyleSheet : function(id, url){
13425        this.removeStyleSheet(id);
13426        var ss = doc.createElement("link");
13427        ss.setAttribute("rel", "stylesheet");
13428        ss.setAttribute("type", "text/css");
13429        ss.setAttribute("id", id);
13430        ss.setAttribute("href", url);
13431        doc.getElementsByTagName("head")[0].appendChild(ss);
13432    },
13433    
13434    /**
13435     * Refresh the rule cache if you have dynamically added stylesheets
13436     * @return {Object} An object (hash) of rules indexed by selector
13437     */
13438    refreshCache : function(){
13439        return this.getRules(true);
13440    },
13441
13442    // private
13443    cacheStyleSheet : function(stylesheet){
13444        if(!rules){
13445            rules = {};
13446        }
13447        try{// try catch for cross domain access issue
13448            var ssRules = stylesheet.cssRules || stylesheet.rules;
13449            for(var j = ssRules.length-1; j >= 0; --j){
13450                rules[ssRules[j].selectorText] = ssRules[j];
13451            }
13452        }catch(e){}
13453    },
13454    
13455    /**
13456     * Gets all css rules for the document
13457     * @param {Boolean} refreshCache true to refresh the internal cache
13458     * @return {Object} An object (hash) of rules indexed by selector
13459     */
13460    getRules : function(refreshCache){
13461                 if(rules == null || refreshCache){
13462                         rules = {};
13463                         var ds = doc.styleSheets;
13464                         for(var i =0, len = ds.length; i < len; i++){
13465                             try{
13466                         this.cacheStyleSheet(ds[i]);
13467                     }catch(e){} 
13468                 }
13469                 }
13470                 return rules;
13471         },
13472         
13473         /**
13474     * Gets an an individual CSS rule by selector(s)
13475     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13476     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13477     * @return {CSSRule} The CSS rule or null if one is not found
13478     */
13479    getRule : function(selector, refreshCache){
13480                 var rs = this.getRules(refreshCache);
13481                 if(!(selector instanceof Array)){
13482                     return rs[selector];
13483                 }
13484                 for(var i = 0; i < selector.length; i++){
13485                         if(rs[selector[i]]){
13486                                 return rs[selector[i]];
13487                         }
13488                 }
13489                 return null;
13490         },
13491         
13492         
13493         /**
13494     * Updates a rule property
13495     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13496     * @param {String} property The css property
13497     * @param {String} value The new value for the property
13498     * @return {Boolean} true If a rule was found and updated
13499     */
13500    updateRule : function(selector, property, value){
13501                 if(!(selector instanceof Array)){
13502                         var rule = this.getRule(selector);
13503                         if(rule){
13504                                 rule.style[property.replace(camelRe, camelFn)] = value;
13505                                 return true;
13506                         }
13507                 }else{
13508                         for(var i = 0; i < selector.length; i++){
13509                                 if(this.updateRule(selector[i], property, value)){
13510                                         return true;
13511                                 }
13512                         }
13513                 }
13514                 return false;
13515         }
13516    };   
13517 }();/*
13518  * Based on:
13519  * Ext JS Library 1.1.1
13520  * Copyright(c) 2006-2007, Ext JS, LLC.
13521  *
13522  * Originally Released Under LGPL - original licence link has changed is not relivant.
13523  *
13524  * Fork - LGPL
13525  * <script type="text/javascript">
13526  */
13527
13528  
13529
13530 /**
13531  * @class Roo.util.ClickRepeater
13532  * @extends Roo.util.Observable
13533  * 
13534  * A wrapper class which can be applied to any element. Fires a "click" event while the
13535  * mouse is pressed. The interval between firings may be specified in the config but
13536  * defaults to 10 milliseconds.
13537  * 
13538  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13539  * 
13540  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13541  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13542  * Similar to an autorepeat key delay.
13543  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13544  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13545  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13546  *           "interval" and "delay" are ignored. "immediate" is honored.
13547  * @cfg {Boolean} preventDefault True to prevent the default click event
13548  * @cfg {Boolean} stopDefault True to stop the default click event
13549  * 
13550  * @history
13551  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13552  *     2007-02-02 jvs Renamed to ClickRepeater
13553  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13554  *
13555  *  @constructor
13556  * @param {String/HTMLElement/Element} el The element to listen on
13557  * @param {Object} config
13558  **/
13559 Roo.util.ClickRepeater = function(el, config)
13560 {
13561     this.el = Roo.get(el);
13562     this.el.unselectable();
13563
13564     Roo.apply(this, config);
13565
13566     this.addEvents({
13567     /**
13568      * @event mousedown
13569      * Fires when the mouse button is depressed.
13570      * @param {Roo.util.ClickRepeater} this
13571      */
13572         "mousedown" : true,
13573     /**
13574      * @event click
13575      * Fires on a specified interval during the time the element is pressed.
13576      * @param {Roo.util.ClickRepeater} this
13577      */
13578         "click" : true,
13579     /**
13580      * @event mouseup
13581      * Fires when the mouse key is released.
13582      * @param {Roo.util.ClickRepeater} this
13583      */
13584         "mouseup" : true
13585     });
13586
13587     this.el.on("mousedown", this.handleMouseDown, this);
13588     if(this.preventDefault || this.stopDefault){
13589         this.el.on("click", function(e){
13590             if(this.preventDefault){
13591                 e.preventDefault();
13592             }
13593             if(this.stopDefault){
13594                 e.stopEvent();
13595             }
13596         }, this);
13597     }
13598
13599     // allow inline handler
13600     if(this.handler){
13601         this.on("click", this.handler,  this.scope || this);
13602     }
13603
13604     Roo.util.ClickRepeater.superclass.constructor.call(this);
13605 };
13606
13607 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13608     interval : 20,
13609     delay: 250,
13610     preventDefault : true,
13611     stopDefault : false,
13612     timer : 0,
13613
13614     // private
13615     handleMouseDown : function(){
13616         clearTimeout(this.timer);
13617         this.el.blur();
13618         if(this.pressClass){
13619             this.el.addClass(this.pressClass);
13620         }
13621         this.mousedownTime = new Date();
13622
13623         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13624         this.el.on("mouseout", this.handleMouseOut, this);
13625
13626         this.fireEvent("mousedown", this);
13627         this.fireEvent("click", this);
13628         
13629         this.timer = this.click.defer(this.delay || this.interval, this);
13630     },
13631
13632     // private
13633     click : function(){
13634         this.fireEvent("click", this);
13635         this.timer = this.click.defer(this.getInterval(), this);
13636     },
13637
13638     // private
13639     getInterval: function(){
13640         if(!this.accelerate){
13641             return this.interval;
13642         }
13643         var pressTime = this.mousedownTime.getElapsed();
13644         if(pressTime < 500){
13645             return 400;
13646         }else if(pressTime < 1700){
13647             return 320;
13648         }else if(pressTime < 2600){
13649             return 250;
13650         }else if(pressTime < 3500){
13651             return 180;
13652         }else if(pressTime < 4400){
13653             return 140;
13654         }else if(pressTime < 5300){
13655             return 80;
13656         }else if(pressTime < 6200){
13657             return 50;
13658         }else{
13659             return 10;
13660         }
13661     },
13662
13663     // private
13664     handleMouseOut : function(){
13665         clearTimeout(this.timer);
13666         if(this.pressClass){
13667             this.el.removeClass(this.pressClass);
13668         }
13669         this.el.on("mouseover", this.handleMouseReturn, this);
13670     },
13671
13672     // private
13673     handleMouseReturn : function(){
13674         this.el.un("mouseover", this.handleMouseReturn);
13675         if(this.pressClass){
13676             this.el.addClass(this.pressClass);
13677         }
13678         this.click();
13679     },
13680
13681     // private
13682     handleMouseUp : function(){
13683         clearTimeout(this.timer);
13684         this.el.un("mouseover", this.handleMouseReturn);
13685         this.el.un("mouseout", this.handleMouseOut);
13686         Roo.get(document).un("mouseup", this.handleMouseUp);
13687         this.el.removeClass(this.pressClass);
13688         this.fireEvent("mouseup", this);
13689     }
13690 });/*
13691  * Based on:
13692  * Ext JS Library 1.1.1
13693  * Copyright(c) 2006-2007, Ext JS, LLC.
13694  *
13695  * Originally Released Under LGPL - original licence link has changed is not relivant.
13696  *
13697  * Fork - LGPL
13698  * <script type="text/javascript">
13699  */
13700
13701  
13702 /**
13703  * @class Roo.KeyNav
13704  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13705  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13706  * way to implement custom navigation schemes for any UI component.</p>
13707  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13708  * pageUp, pageDown, del, home, end.  Usage:</p>
13709  <pre><code>
13710 var nav = new Roo.KeyNav("my-element", {
13711     "left" : function(e){
13712         this.moveLeft(e.ctrlKey);
13713     },
13714     "right" : function(e){
13715         this.moveRight(e.ctrlKey);
13716     },
13717     "enter" : function(e){
13718         this.save();
13719     },
13720     scope : this
13721 });
13722 </code></pre>
13723  * @constructor
13724  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13725  * @param {Object} config The config
13726  */
13727 Roo.KeyNav = function(el, config){
13728     this.el = Roo.get(el);
13729     Roo.apply(this, config);
13730     if(!this.disabled){
13731         this.disabled = true;
13732         this.enable();
13733     }
13734 };
13735
13736 Roo.KeyNav.prototype = {
13737     /**
13738      * @cfg {Boolean} disabled
13739      * True to disable this KeyNav instance (defaults to false)
13740      */
13741     disabled : false,
13742     /**
13743      * @cfg {String} defaultEventAction
13744      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13745      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13746      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13747      */
13748     defaultEventAction: "stopEvent",
13749     /**
13750      * @cfg {Boolean} forceKeyDown
13751      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13752      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13753      * handle keydown instead of keypress.
13754      */
13755     forceKeyDown : false,
13756
13757     // private
13758     prepareEvent : function(e){
13759         var k = e.getKey();
13760         var h = this.keyToHandler[k];
13761         //if(h && this[h]){
13762         //    e.stopPropagation();
13763         //}
13764         if(Roo.isSafari && h && k >= 37 && k <= 40){
13765             e.stopEvent();
13766         }
13767     },
13768
13769     // private
13770     relay : function(e){
13771         var k = e.getKey();
13772         var h = this.keyToHandler[k];
13773         if(h && this[h]){
13774             if(this.doRelay(e, this[h], h) !== true){
13775                 e[this.defaultEventAction]();
13776             }
13777         }
13778     },
13779
13780     // private
13781     doRelay : function(e, h, hname){
13782         return h.call(this.scope || this, e);
13783     },
13784
13785     // possible handlers
13786     enter : false,
13787     left : false,
13788     right : false,
13789     up : false,
13790     down : false,
13791     tab : false,
13792     esc : false,
13793     pageUp : false,
13794     pageDown : false,
13795     del : false,
13796     home : false,
13797     end : false,
13798
13799     // quick lookup hash
13800     keyToHandler : {
13801         37 : "left",
13802         39 : "right",
13803         38 : "up",
13804         40 : "down",
13805         33 : "pageUp",
13806         34 : "pageDown",
13807         46 : "del",
13808         36 : "home",
13809         35 : "end",
13810         13 : "enter",
13811         27 : "esc",
13812         9  : "tab"
13813     },
13814
13815         /**
13816          * Enable this KeyNav
13817          */
13818         enable: function(){
13819                 if(this.disabled){
13820             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13821             // the EventObject will normalize Safari automatically
13822             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13823                 this.el.on("keydown", this.relay,  this);
13824             }else{
13825                 this.el.on("keydown", this.prepareEvent,  this);
13826                 this.el.on("keypress", this.relay,  this);
13827             }
13828                     this.disabled = false;
13829                 }
13830         },
13831
13832         /**
13833          * Disable this KeyNav
13834          */
13835         disable: function(){
13836                 if(!this.disabled){
13837                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13838                 this.el.un("keydown", this.relay);
13839             }else{
13840                 this.el.un("keydown", this.prepareEvent);
13841                 this.el.un("keypress", this.relay);
13842             }
13843                     this.disabled = true;
13844                 }
13845         }
13846 };/*
13847  * Based on:
13848  * Ext JS Library 1.1.1
13849  * Copyright(c) 2006-2007, Ext JS, LLC.
13850  *
13851  * Originally Released Under LGPL - original licence link has changed is not relivant.
13852  *
13853  * Fork - LGPL
13854  * <script type="text/javascript">
13855  */
13856
13857  
13858 /**
13859  * @class Roo.KeyMap
13860  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13861  * The constructor accepts the same config object as defined by {@link #addBinding}.
13862  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13863  * combination it will call the function with this signature (if the match is a multi-key
13864  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13865  * A KeyMap can also handle a string representation of keys.<br />
13866  * Usage:
13867  <pre><code>
13868 // map one key by key code
13869 var map = new Roo.KeyMap("my-element", {
13870     key: 13, // or Roo.EventObject.ENTER
13871     fn: myHandler,
13872     scope: myObject
13873 });
13874
13875 // map multiple keys to one action by string
13876 var map = new Roo.KeyMap("my-element", {
13877     key: "a\r\n\t",
13878     fn: myHandler,
13879     scope: myObject
13880 });
13881
13882 // map multiple keys to multiple actions by strings and array of codes
13883 var map = new Roo.KeyMap("my-element", [
13884     {
13885         key: [10,13],
13886         fn: function(){ alert("Return was pressed"); }
13887     }, {
13888         key: "abc",
13889         fn: function(){ alert('a, b or c was pressed'); }
13890     }, {
13891         key: "\t",
13892         ctrl:true,
13893         shift:true,
13894         fn: function(){ alert('Control + shift + tab was pressed.'); }
13895     }
13896 ]);
13897 </code></pre>
13898  * <b>Note: A KeyMap starts enabled</b>
13899  * @constructor
13900  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13901  * @param {Object} config The config (see {@link #addBinding})
13902  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13903  */
13904 Roo.KeyMap = function(el, config, eventName){
13905     this.el  = Roo.get(el);
13906     this.eventName = eventName || "keydown";
13907     this.bindings = [];
13908     if(config){
13909         this.addBinding(config);
13910     }
13911     this.enable();
13912 };
13913
13914 Roo.KeyMap.prototype = {
13915     /**
13916      * True to stop the event from bubbling and prevent the default browser action if the
13917      * key was handled by the KeyMap (defaults to false)
13918      * @type Boolean
13919      */
13920     stopEvent : false,
13921
13922     /**
13923      * Add a new binding to this KeyMap. The following config object properties are supported:
13924      * <pre>
13925 Property    Type             Description
13926 ----------  ---------------  ----------------------------------------------------------------------
13927 key         String/Array     A single keycode or an array of keycodes to handle
13928 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13929 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13930 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13931 fn          Function         The function to call when KeyMap finds the expected key combination
13932 scope       Object           The scope of the callback function
13933 </pre>
13934      *
13935      * Usage:
13936      * <pre><code>
13937 // Create a KeyMap
13938 var map = new Roo.KeyMap(document, {
13939     key: Roo.EventObject.ENTER,
13940     fn: handleKey,
13941     scope: this
13942 });
13943
13944 //Add a new binding to the existing KeyMap later
13945 map.addBinding({
13946     key: 'abc',
13947     shift: true,
13948     fn: handleKey,
13949     scope: this
13950 });
13951 </code></pre>
13952      * @param {Object/Array} config A single KeyMap config or an array of configs
13953      */
13954         addBinding : function(config){
13955         if(config instanceof Array){
13956             for(var i = 0, len = config.length; i < len; i++){
13957                 this.addBinding(config[i]);
13958             }
13959             return;
13960         }
13961         var keyCode = config.key,
13962             shift = config.shift, 
13963             ctrl = config.ctrl, 
13964             alt = config.alt,
13965             fn = config.fn,
13966             scope = config.scope;
13967         if(typeof keyCode == "string"){
13968             var ks = [];
13969             var keyString = keyCode.toUpperCase();
13970             for(var j = 0, len = keyString.length; j < len; j++){
13971                 ks.push(keyString.charCodeAt(j));
13972             }
13973             keyCode = ks;
13974         }
13975         var keyArray = keyCode instanceof Array;
13976         var handler = function(e){
13977             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13978                 var k = e.getKey();
13979                 if(keyArray){
13980                     for(var i = 0, len = keyCode.length; i < len; i++){
13981                         if(keyCode[i] == k){
13982                           if(this.stopEvent){
13983                               e.stopEvent();
13984                           }
13985                           fn.call(scope || window, k, e);
13986                           return;
13987                         }
13988                     }
13989                 }else{
13990                     if(k == keyCode){
13991                         if(this.stopEvent){
13992                            e.stopEvent();
13993                         }
13994                         fn.call(scope || window, k, e);
13995                     }
13996                 }
13997             }
13998         };
13999         this.bindings.push(handler);  
14000         },
14001
14002     /**
14003      * Shorthand for adding a single key listener
14004      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14005      * following options:
14006      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14007      * @param {Function} fn The function to call
14008      * @param {Object} scope (optional) The scope of the function
14009      */
14010     on : function(key, fn, scope){
14011         var keyCode, shift, ctrl, alt;
14012         if(typeof key == "object" && !(key instanceof Array)){
14013             keyCode = key.key;
14014             shift = key.shift;
14015             ctrl = key.ctrl;
14016             alt = key.alt;
14017         }else{
14018             keyCode = key;
14019         }
14020         this.addBinding({
14021             key: keyCode,
14022             shift: shift,
14023             ctrl: ctrl,
14024             alt: alt,
14025             fn: fn,
14026             scope: scope
14027         })
14028     },
14029
14030     // private
14031     handleKeyDown : function(e){
14032             if(this.enabled){ //just in case
14033             var b = this.bindings;
14034             for(var i = 0, len = b.length; i < len; i++){
14035                 b[i].call(this, e);
14036             }
14037             }
14038         },
14039         
14040         /**
14041          * Returns true if this KeyMap is enabled
14042          * @return {Boolean} 
14043          */
14044         isEnabled : function(){
14045             return this.enabled;  
14046         },
14047         
14048         /**
14049          * Enables this KeyMap
14050          */
14051         enable: function(){
14052                 if(!this.enabled){
14053                     this.el.on(this.eventName, this.handleKeyDown, this);
14054                     this.enabled = true;
14055                 }
14056         },
14057
14058         /**
14059          * Disable this KeyMap
14060          */
14061         disable: function(){
14062                 if(this.enabled){
14063                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14064                     this.enabled = false;
14065                 }
14066         }
14067 };/*
14068  * Based on:
14069  * Ext JS Library 1.1.1
14070  * Copyright(c) 2006-2007, Ext JS, LLC.
14071  *
14072  * Originally Released Under LGPL - original licence link has changed is not relivant.
14073  *
14074  * Fork - LGPL
14075  * <script type="text/javascript">
14076  */
14077
14078  
14079 /**
14080  * @class Roo.util.TextMetrics
14081  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14082  * wide, in pixels, a given block of text will be.
14083  * @singleton
14084  */
14085 Roo.util.TextMetrics = function(){
14086     var shared;
14087     return {
14088         /**
14089          * Measures the size of the specified text
14090          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14091          * that can affect the size of the rendered text
14092          * @param {String} text The text to measure
14093          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14094          * in order to accurately measure the text height
14095          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14096          */
14097         measure : function(el, text, fixedWidth){
14098             if(!shared){
14099                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14100             }
14101             shared.bind(el);
14102             shared.setFixedWidth(fixedWidth || 'auto');
14103             return shared.getSize(text);
14104         },
14105
14106         /**
14107          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14108          * the overhead of multiple calls to initialize the style properties on each measurement.
14109          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14110          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14111          * in order to accurately measure the text height
14112          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14113          */
14114         createInstance : function(el, fixedWidth){
14115             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14116         }
14117     };
14118 }();
14119
14120  
14121
14122 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14123     var ml = new Roo.Element(document.createElement('div'));
14124     document.body.appendChild(ml.dom);
14125     ml.position('absolute');
14126     ml.setLeftTop(-1000, -1000);
14127     ml.hide();
14128
14129     if(fixedWidth){
14130         ml.setWidth(fixedWidth);
14131     }
14132      
14133     var instance = {
14134         /**
14135          * Returns the size of the specified text based on the internal element's style and width properties
14136          * @memberOf Roo.util.TextMetrics.Instance#
14137          * @param {String} text The text to measure
14138          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14139          */
14140         getSize : function(text){
14141             ml.update(text);
14142             var s = ml.getSize();
14143             ml.update('');
14144             return s;
14145         },
14146
14147         /**
14148          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14149          * that can affect the size of the rendered text
14150          * @memberOf Roo.util.TextMetrics.Instance#
14151          * @param {String/HTMLElement} el The element, dom node or id
14152          */
14153         bind : function(el){
14154             ml.setStyle(
14155                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14156             );
14157         },
14158
14159         /**
14160          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14161          * to set a fixed width in order to accurately measure the text height.
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {Number} width The width to set on the element
14164          */
14165         setFixedWidth : function(width){
14166             ml.setWidth(width);
14167         },
14168
14169         /**
14170          * Returns the measured width of the specified text
14171          * @memberOf Roo.util.TextMetrics.Instance#
14172          * @param {String} text The text to measure
14173          * @return {Number} width The width in pixels
14174          */
14175         getWidth : function(text){
14176             ml.dom.style.width = 'auto';
14177             return this.getSize(text).width;
14178         },
14179
14180         /**
14181          * Returns the measured height of the specified text.  For multiline text, be sure to call
14182          * {@link #setFixedWidth} if necessary.
14183          * @memberOf Roo.util.TextMetrics.Instance#
14184          * @param {String} text The text to measure
14185          * @return {Number} height The height in pixels
14186          */
14187         getHeight : function(text){
14188             return this.getSize(text).height;
14189         }
14190     };
14191
14192     instance.bind(bindTo);
14193
14194     return instance;
14195 };
14196
14197 // backwards compat
14198 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209 /**
14210  * @class Roo.state.Provider
14211  * Abstract base class for state provider implementations. This class provides methods
14212  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14213  * Provider interface.
14214  */
14215 Roo.state.Provider = function(){
14216     /**
14217      * @event statechange
14218      * Fires when a state change occurs.
14219      * @param {Provider} this This state provider
14220      * @param {String} key The state key which was changed
14221      * @param {String} value The encoded value for the state
14222      */
14223     this.addEvents({
14224         "statechange": true
14225     });
14226     this.state = {};
14227     Roo.state.Provider.superclass.constructor.call(this);
14228 };
14229 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14230     /**
14231      * Returns the current value for a key
14232      * @param {String} name The key name
14233      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14234      * @return {Mixed} The state data
14235      */
14236     get : function(name, defaultValue){
14237         return typeof this.state[name] == "undefined" ?
14238             defaultValue : this.state[name];
14239     },
14240     
14241     /**
14242      * Clears a value from the state
14243      * @param {String} name The key name
14244      */
14245     clear : function(name){
14246         delete this.state[name];
14247         this.fireEvent("statechange", this, name, null);
14248     },
14249     
14250     /**
14251      * Sets the value for a key
14252      * @param {String} name The key name
14253      * @param {Mixed} value The value to set
14254      */
14255     set : function(name, value){
14256         this.state[name] = value;
14257         this.fireEvent("statechange", this, name, value);
14258     },
14259     
14260     /**
14261      * Decodes a string previously encoded with {@link #encodeValue}.
14262      * @param {String} value The value to decode
14263      * @return {Mixed} The decoded value
14264      */
14265     decodeValue : function(cookie){
14266         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14267         var matches = re.exec(unescape(cookie));
14268         if(!matches || !matches[1]) return; // non state cookie
14269         var type = matches[1];
14270         var v = matches[2];
14271         switch(type){
14272             case "n":
14273                 return parseFloat(v);
14274             case "d":
14275                 return new Date(Date.parse(v));
14276             case "b":
14277                 return (v == "1");
14278             case "a":
14279                 var all = [];
14280                 var values = v.split("^");
14281                 for(var i = 0, len = values.length; i < len; i++){
14282                     all.push(this.decodeValue(values[i]));
14283                 }
14284                 return all;
14285            case "o":
14286                 var all = {};
14287                 var values = v.split("^");
14288                 for(var i = 0, len = values.length; i < len; i++){
14289                     var kv = values[i].split("=");
14290                     all[kv[0]] = this.decodeValue(kv[1]);
14291                 }
14292                 return all;
14293            default:
14294                 return v;
14295         }
14296     },
14297     
14298     /**
14299      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14300      * @param {Mixed} value The value to encode
14301      * @return {String} The encoded value
14302      */
14303     encodeValue : function(v){
14304         var enc;
14305         if(typeof v == "number"){
14306             enc = "n:" + v;
14307         }else if(typeof v == "boolean"){
14308             enc = "b:" + (v ? "1" : "0");
14309         }else if(v instanceof Date){
14310             enc = "d:" + v.toGMTString();
14311         }else if(v instanceof Array){
14312             var flat = "";
14313             for(var i = 0, len = v.length; i < len; i++){
14314                 flat += this.encodeValue(v[i]);
14315                 if(i != len-1) flat += "^";
14316             }
14317             enc = "a:" + flat;
14318         }else if(typeof v == "object"){
14319             var flat = "";
14320             for(var key in v){
14321                 if(typeof v[key] != "function"){
14322                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14323                 }
14324             }
14325             enc = "o:" + flat.substring(0, flat.length-1);
14326         }else{
14327             enc = "s:" + v;
14328         }
14329         return escape(enc);        
14330     }
14331 });
14332
14333 /*
14334  * Based on:
14335  * Ext JS Library 1.1.1
14336  * Copyright(c) 2006-2007, Ext JS, LLC.
14337  *
14338  * Originally Released Under LGPL - original licence link has changed is not relivant.
14339  *
14340  * Fork - LGPL
14341  * <script type="text/javascript">
14342  */
14343 /**
14344  * @class Roo.state.Manager
14345  * This is the global state manager. By default all components that are "state aware" check this class
14346  * for state information if you don't pass them a custom state provider. In order for this class
14347  * to be useful, it must be initialized with a provider when your application initializes.
14348  <pre><code>
14349 // in your initialization function
14350 init : function(){
14351    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14352    ...
14353    // supposed you have a {@link Roo.BorderLayout}
14354    var layout = new Roo.BorderLayout(...);
14355    layout.restoreState();
14356    // or a {Roo.BasicDialog}
14357    var dialog = new Roo.BasicDialog(...);
14358    dialog.restoreState();
14359  </code></pre>
14360  * @singleton
14361  */
14362 Roo.state.Manager = function(){
14363     var provider = new Roo.state.Provider();
14364     
14365     return {
14366         /**
14367          * Configures the default state provider for your application
14368          * @param {Provider} stateProvider The state provider to set
14369          */
14370         setProvider : function(stateProvider){
14371             provider = stateProvider;
14372         },
14373         
14374         /**
14375          * Returns the current value for a key
14376          * @param {String} name The key name
14377          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14378          * @return {Mixed} The state data
14379          */
14380         get : function(key, defaultValue){
14381             return provider.get(key, defaultValue);
14382         },
14383         
14384         /**
14385          * Sets the value for a key
14386          * @param {String} name The key name
14387          * @param {Mixed} value The state data
14388          */
14389          set : function(key, value){
14390             provider.set(key, value);
14391         },
14392         
14393         /**
14394          * Clears a value from the state
14395          * @param {String} name The key name
14396          */
14397         clear : function(key){
14398             provider.clear(key);
14399         },
14400         
14401         /**
14402          * Gets the currently configured state provider
14403          * @return {Provider} The state provider
14404          */
14405         getProvider : function(){
14406             return provider;
14407         }
14408     };
14409 }();
14410 /*
14411  * Based on:
14412  * Ext JS Library 1.1.1
14413  * Copyright(c) 2006-2007, Ext JS, LLC.
14414  *
14415  * Originally Released Under LGPL - original licence link has changed is not relivant.
14416  *
14417  * Fork - LGPL
14418  * <script type="text/javascript">
14419  */
14420 /**
14421  * @class Roo.state.CookieProvider
14422  * @extends Roo.state.Provider
14423  * The default Provider implementation which saves state via cookies.
14424  * <br />Usage:
14425  <pre><code>
14426    var cp = new Roo.state.CookieProvider({
14427        path: "/cgi-bin/",
14428        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14429        domain: "roojs.com"
14430    })
14431    Roo.state.Manager.setProvider(cp);
14432  </code></pre>
14433  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14434  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14435  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14436  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14437  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14438  * domain the page is running on including the 'www' like 'www.roojs.com')
14439  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14440  * @constructor
14441  * Create a new CookieProvider
14442  * @param {Object} config The configuration object
14443  */
14444 Roo.state.CookieProvider = function(config){
14445     Roo.state.CookieProvider.superclass.constructor.call(this);
14446     this.path = "/";
14447     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14448     this.domain = null;
14449     this.secure = false;
14450     Roo.apply(this, config);
14451     this.state = this.readCookies();
14452 };
14453
14454 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14455     // private
14456     set : function(name, value){
14457         if(typeof value == "undefined" || value === null){
14458             this.clear(name);
14459             return;
14460         }
14461         this.setCookie(name, value);
14462         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14463     },
14464
14465     // private
14466     clear : function(name){
14467         this.clearCookie(name);
14468         Roo.state.CookieProvider.superclass.clear.call(this, name);
14469     },
14470
14471     // private
14472     readCookies : function(){
14473         var cookies = {};
14474         var c = document.cookie + ";";
14475         var re = /\s?(.*?)=(.*?);/g;
14476         var matches;
14477         while((matches = re.exec(c)) != null){
14478             var name = matches[1];
14479             var value = matches[2];
14480             if(name && name.substring(0,3) == "ys-"){
14481                 cookies[name.substr(3)] = this.decodeValue(value);
14482             }
14483         }
14484         return cookies;
14485     },
14486
14487     // private
14488     setCookie : function(name, value){
14489         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14490            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14491            ((this.path == null) ? "" : ("; path=" + this.path)) +
14492            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14493            ((this.secure == true) ? "; secure" : "");
14494     },
14495
14496     // private
14497     clearCookie : function(name){
14498         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14499            ((this.path == null) ? "" : ("; path=" + this.path)) +
14500            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14501            ((this.secure == true) ? "; secure" : "");
14502     }
14503 });/*
14504  * Based on:
14505  * Ext JS Library 1.1.1
14506  * Copyright(c) 2006-2007, Ext JS, LLC.
14507  *
14508  * Originally Released Under LGPL - original licence link has changed is not relivant.
14509  *
14510  * Fork - LGPL
14511  * <script type="text/javascript">
14512  */
14513
14514
14515
14516 /*
14517  * These classes are derivatives of the similarly named classes in the YUI Library.
14518  * The original license:
14519  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14520  * Code licensed under the BSD License:
14521  * http://developer.yahoo.net/yui/license.txt
14522  */
14523
14524 (function() {
14525
14526 var Event=Roo.EventManager;
14527 var Dom=Roo.lib.Dom;
14528
14529 /**
14530  * @class Roo.dd.DragDrop
14531  * @extends Roo.util.Observable
14532  * Defines the interface and base operation of items that that can be
14533  * dragged or can be drop targets.  It was designed to be extended, overriding
14534  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14535  * Up to three html elements can be associated with a DragDrop instance:
14536  * <ul>
14537  * <li>linked element: the element that is passed into the constructor.
14538  * This is the element which defines the boundaries for interaction with
14539  * other DragDrop objects.</li>
14540  * <li>handle element(s): The drag operation only occurs if the element that
14541  * was clicked matches a handle element.  By default this is the linked
14542  * element, but there are times that you will want only a portion of the
14543  * linked element to initiate the drag operation, and the setHandleElId()
14544  * method provides a way to define this.</li>
14545  * <li>drag element: this represents the element that would be moved along
14546  * with the cursor during a drag operation.  By default, this is the linked
14547  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14548  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14549  * </li>
14550  * </ul>
14551  * This class should not be instantiated until the onload event to ensure that
14552  * the associated elements are available.
14553  * The following would define a DragDrop obj that would interact with any
14554  * other DragDrop obj in the "group1" group:
14555  * <pre>
14556  *  dd = new Roo.dd.DragDrop("div1", "group1");
14557  * </pre>
14558  * Since none of the event handlers have been implemented, nothing would
14559  * actually happen if you were to run the code above.  Normally you would
14560  * override this class or one of the default implementations, but you can
14561  * also override the methods you want on an instance of the class...
14562  * <pre>
14563  *  dd.onDragDrop = function(e, id) {
14564  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14565  *  }
14566  * </pre>
14567  * @constructor
14568  * @param {String} id of the element that is linked to this instance
14569  * @param {String} sGroup the group of related DragDrop objects
14570  * @param {object} config an object containing configurable attributes
14571  *                Valid properties for DragDrop:
14572  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14573  */
14574 Roo.dd.DragDrop = function(id, sGroup, config) {
14575     if (id) {
14576         this.init(id, sGroup, config);
14577     }
14578     
14579 };
14580
14581 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14582
14583     /**
14584      * The id of the element associated with this object.  This is what we
14585      * refer to as the "linked element" because the size and position of
14586      * this element is used to determine when the drag and drop objects have
14587      * interacted.
14588      * @property id
14589      * @type String
14590      */
14591     id: null,
14592
14593     /**
14594      * Configuration attributes passed into the constructor
14595      * @property config
14596      * @type object
14597      */
14598     config: null,
14599
14600     /**
14601      * The id of the element that will be dragged.  By default this is same
14602      * as the linked element , but could be changed to another element. Ex:
14603      * Roo.dd.DDProxy
14604      * @property dragElId
14605      * @type String
14606      * @private
14607      */
14608     dragElId: null,
14609
14610     /**
14611      * the id of the element that initiates the drag operation.  By default
14612      * this is the linked element, but could be changed to be a child of this
14613      * element.  This lets us do things like only starting the drag when the
14614      * header element within the linked html element is clicked.
14615      * @property handleElId
14616      * @type String
14617      * @private
14618      */
14619     handleElId: null,
14620
14621     /**
14622      * An associative array of HTML tags that will be ignored if clicked.
14623      * @property invalidHandleTypes
14624      * @type {string: string}
14625      */
14626     invalidHandleTypes: null,
14627
14628     /**
14629      * An associative array of ids for elements that will be ignored if clicked
14630      * @property invalidHandleIds
14631      * @type {string: string}
14632      */
14633     invalidHandleIds: null,
14634
14635     /**
14636      * An indexted array of css class names for elements that will be ignored
14637      * if clicked.
14638      * @property invalidHandleClasses
14639      * @type string[]
14640      */
14641     invalidHandleClasses: null,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageX
14647      * @type int
14648      * @private
14649      */
14650     startPageX: 0,
14651
14652     /**
14653      * The linked element's absolute X position at the time the drag was
14654      * started
14655      * @property startPageY
14656      * @type int
14657      * @private
14658      */
14659     startPageY: 0,
14660
14661     /**
14662      * The group defines a logical collection of DragDrop objects that are
14663      * related.  Instances only get events when interacting with other
14664      * DragDrop object in the same group.  This lets us define multiple
14665      * groups using a single DragDrop subclass if we want.
14666      * @property groups
14667      * @type {string: string}
14668      */
14669     groups: null,
14670
14671     /**
14672      * Individual drag/drop instances can be locked.  This will prevent
14673      * onmousedown start drag.
14674      * @property locked
14675      * @type boolean
14676      * @private
14677      */
14678     locked: false,
14679
14680     /**
14681      * Lock this instance
14682      * @method lock
14683      */
14684     lock: function() { this.locked = true; },
14685
14686     /**
14687      * Unlock this instace
14688      * @method unlock
14689      */
14690     unlock: function() { this.locked = false; },
14691
14692     /**
14693      * By default, all insances can be a drop target.  This can be disabled by
14694      * setting isTarget to false.
14695      * @method isTarget
14696      * @type boolean
14697      */
14698     isTarget: true,
14699
14700     /**
14701      * The padding configured for this drag and drop object for calculating
14702      * the drop zone intersection with this object.
14703      * @method padding
14704      * @type int[]
14705      */
14706     padding: null,
14707
14708     /**
14709      * Cached reference to the linked element
14710      * @property _domRef
14711      * @private
14712      */
14713     _domRef: null,
14714
14715     /**
14716      * Internal typeof flag
14717      * @property __ygDragDrop
14718      * @private
14719      */
14720     __ygDragDrop: true,
14721
14722     /**
14723      * Set to true when horizontal contraints are applied
14724      * @property constrainX
14725      * @type boolean
14726      * @private
14727      */
14728     constrainX: false,
14729
14730     /**
14731      * Set to true when vertical contraints are applied
14732      * @property constrainY
14733      * @type boolean
14734      * @private
14735      */
14736     constrainY: false,
14737
14738     /**
14739      * The left constraint
14740      * @property minX
14741      * @type int
14742      * @private
14743      */
14744     minX: 0,
14745
14746     /**
14747      * The right constraint
14748      * @property maxX
14749      * @type int
14750      * @private
14751      */
14752     maxX: 0,
14753
14754     /**
14755      * The up constraint
14756      * @property minY
14757      * @type int
14758      * @type int
14759      * @private
14760      */
14761     minY: 0,
14762
14763     /**
14764      * The down constraint
14765      * @property maxY
14766      * @type int
14767      * @private
14768      */
14769     maxY: 0,
14770
14771     /**
14772      * Maintain offsets when we resetconstraints.  Set to true when you want
14773      * the position of the element relative to its parent to stay the same
14774      * when the page changes
14775      *
14776      * @property maintainOffset
14777      * @type boolean
14778      */
14779     maintainOffset: false,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * horizontal graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property xTicks
14786      * @type int[]
14787      */
14788     xTicks: null,
14789
14790     /**
14791      * Array of pixel locations the element will snap to if we specified a
14792      * vertical graduation/interval.  This array is generated automatically
14793      * when you define a tick interval.
14794      * @property yTicks
14795      * @type int[]
14796      */
14797     yTicks: null,
14798
14799     /**
14800      * By default the drag and drop instance will only respond to the primary
14801      * button click (left button for a right-handed mouse).  Set to true to
14802      * allow drag and drop to start with any mouse click that is propogated
14803      * by the browser
14804      * @property primaryButtonOnly
14805      * @type boolean
14806      */
14807     primaryButtonOnly: true,
14808
14809     /**
14810      * The availabe property is false until the linked dom element is accessible.
14811      * @property available
14812      * @type boolean
14813      */
14814     available: false,
14815
14816     /**
14817      * By default, drags can only be initiated if the mousedown occurs in the
14818      * region the linked element is.  This is done in part to work around a
14819      * bug in some browsers that mis-report the mousedown if the previous
14820      * mouseup happened outside of the window.  This property is set to true
14821      * if outer handles are defined.
14822      *
14823      * @property hasOuterHandles
14824      * @type boolean
14825      * @default false
14826      */
14827     hasOuterHandles: false,
14828
14829     /**
14830      * Code that executes immediately before the startDrag event
14831      * @method b4StartDrag
14832      * @private
14833      */
14834     b4StartDrag: function(x, y) { },
14835
14836     /**
14837      * Abstract method called after a drag/drop object is clicked
14838      * and the drag or mousedown time thresholds have beeen met.
14839      * @method startDrag
14840      * @param {int} X click location
14841      * @param {int} Y click location
14842      */
14843     startDrag: function(x, y) { /* override this */ },
14844
14845     /**
14846      * Code that executes immediately before the onDrag event
14847      * @method b4Drag
14848      * @private
14849      */
14850     b4Drag: function(e) { },
14851
14852     /**
14853      * Abstract method called during the onMouseMove event while dragging an
14854      * object.
14855      * @method onDrag
14856      * @param {Event} e the mousemove event
14857      */
14858     onDrag: function(e) { /* override this */ },
14859
14860     /**
14861      * Abstract method called when this element fist begins hovering over
14862      * another DragDrop obj
14863      * @method onDragEnter
14864      * @param {Event} e the mousemove event
14865      * @param {String|DragDrop[]} id In POINT mode, the element
14866      * id this is hovering over.  In INTERSECT mode, an array of one or more
14867      * dragdrop items being hovered over.
14868      */
14869     onDragEnter: function(e, id) { /* override this */ },
14870
14871     /**
14872      * Code that executes immediately before the onDragOver event
14873      * @method b4DragOver
14874      * @private
14875      */
14876     b4DragOver: function(e) { },
14877
14878     /**
14879      * Abstract method called when this element is hovering over another
14880      * DragDrop obj
14881      * @method onDragOver
14882      * @param {Event} e the mousemove event
14883      * @param {String|DragDrop[]} id In POINT mode, the element
14884      * id this is hovering over.  In INTERSECT mode, an array of dd items
14885      * being hovered over.
14886      */
14887     onDragOver: function(e, id) { /* override this */ },
14888
14889     /**
14890      * Code that executes immediately before the onDragOut event
14891      * @method b4DragOut
14892      * @private
14893      */
14894     b4DragOut: function(e) { },
14895
14896     /**
14897      * Abstract method called when we are no longer hovering over an element
14898      * @method onDragOut
14899      * @param {Event} e the mousemove event
14900      * @param {String|DragDrop[]} id In POINT mode, the element
14901      * id this was hovering over.  In INTERSECT mode, an array of dd items
14902      * that the mouse is no longer over.
14903      */
14904     onDragOut: function(e, id) { /* override this */ },
14905
14906     /**
14907      * Code that executes immediately before the onDragDrop event
14908      * @method b4DragDrop
14909      * @private
14910      */
14911     b4DragDrop: function(e) { },
14912
14913     /**
14914      * Abstract method called when this item is dropped on another DragDrop
14915      * obj
14916      * @method onDragDrop
14917      * @param {Event} e the mouseup event
14918      * @param {String|DragDrop[]} id In POINT mode, the element
14919      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14920      * was dropped on.
14921      */
14922     onDragDrop: function(e, id) { /* override this */ },
14923
14924     /**
14925      * Abstract method called when this item is dropped on an area with no
14926      * drop target
14927      * @method onInvalidDrop
14928      * @param {Event} e the mouseup event
14929      */
14930     onInvalidDrop: function(e) { /* override this */ },
14931
14932     /**
14933      * Code that executes immediately before the endDrag event
14934      * @method b4EndDrag
14935      * @private
14936      */
14937     b4EndDrag: function(e) { },
14938
14939     /**
14940      * Fired when we are done dragging the object
14941      * @method endDrag
14942      * @param {Event} e the mouseup event
14943      */
14944     endDrag: function(e) { /* override this */ },
14945
14946     /**
14947      * Code executed immediately before the onMouseDown event
14948      * @method b4MouseDown
14949      * @param {Event} e the mousedown event
14950      * @private
14951      */
14952     b4MouseDown: function(e) {  },
14953
14954     /**
14955      * Event handler that fires when a drag/drop obj gets a mousedown
14956      * @method onMouseDown
14957      * @param {Event} e the mousedown event
14958      */
14959     onMouseDown: function(e) { /* override this */ },
14960
14961     /**
14962      * Event handler that fires when a drag/drop obj gets a mouseup
14963      * @method onMouseUp
14964      * @param {Event} e the mouseup event
14965      */
14966     onMouseUp: function(e) { /* override this */ },
14967
14968     /**
14969      * Override the onAvailable method to do what is needed after the initial
14970      * position was determined.
14971      * @method onAvailable
14972      */
14973     onAvailable: function () {
14974     },
14975
14976     /*
14977      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14978      * @type Object
14979      */
14980     defaultPadding : {left:0, right:0, top:0, bottom:0},
14981
14982     /*
14983      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14984  *
14985  * Usage:
14986  <pre><code>
14987  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14988                 { dragElId: "existingProxyDiv" });
14989  dd.startDrag = function(){
14990      this.constrainTo("parent-id");
14991  };
14992  </code></pre>
14993  * Or you can initalize it using the {@link Roo.Element} object:
14994  <pre><code>
14995  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14996      startDrag : function(){
14997          this.constrainTo("parent-id");
14998      }
14999  });
15000  </code></pre>
15001      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15002      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15003      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15004      * an object containing the sides to pad. For example: {right:10, bottom:10}
15005      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15006      */
15007     constrainTo : function(constrainTo, pad, inContent){
15008         if(typeof pad == "number"){
15009             pad = {left: pad, right:pad, top:pad, bottom:pad};
15010         }
15011         pad = pad || this.defaultPadding;
15012         var b = Roo.get(this.getEl()).getBox();
15013         var ce = Roo.get(constrainTo);
15014         var s = ce.getScroll();
15015         var c, cd = ce.dom;
15016         if(cd == document.body){
15017             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15018         }else{
15019             xy = ce.getXY();
15020             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15021         }
15022
15023
15024         var topSpace = b.y - c.y;
15025         var leftSpace = b.x - c.x;
15026
15027         this.resetConstraints();
15028         this.setXConstraint(leftSpace - (pad.left||0), // left
15029                 c.width - leftSpace - b.width - (pad.right||0) //right
15030         );
15031         this.setYConstraint(topSpace - (pad.top||0), //top
15032                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15033         );
15034     },
15035
15036     /**
15037      * Returns a reference to the linked element
15038      * @method getEl
15039      * @return {HTMLElement} the html element
15040      */
15041     getEl: function() {
15042         if (!this._domRef) {
15043             this._domRef = Roo.getDom(this.id);
15044         }
15045
15046         return this._domRef;
15047     },
15048
15049     /**
15050      * Returns a reference to the actual element to drag.  By default this is
15051      * the same as the html element, but it can be assigned to another
15052      * element. An example of this can be found in Roo.dd.DDProxy
15053      * @method getDragEl
15054      * @return {HTMLElement} the html element
15055      */
15056     getDragEl: function() {
15057         return Roo.getDom(this.dragElId);
15058     },
15059
15060     /**
15061      * Sets up the DragDrop object.  Must be called in the constructor of any
15062      * Roo.dd.DragDrop subclass
15063      * @method init
15064      * @param id the id of the linked element
15065      * @param {String} sGroup the group of related items
15066      * @param {object} config configuration attributes
15067      */
15068     init: function(id, sGroup, config) {
15069         this.initTarget(id, sGroup, config);
15070         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15071         // Event.on(this.id, "selectstart", Event.preventDefault);
15072     },
15073
15074     /**
15075      * Initializes Targeting functionality only... the object does not
15076      * get a mousedown handler.
15077      * @method initTarget
15078      * @param id the id of the linked element
15079      * @param {String} sGroup the group of related items
15080      * @param {object} config configuration attributes
15081      */
15082     initTarget: function(id, sGroup, config) {
15083
15084         // configuration attributes
15085         this.config = config || {};
15086
15087         // create a local reference to the drag and drop manager
15088         this.DDM = Roo.dd.DDM;
15089         // initialize the groups array
15090         this.groups = {};
15091
15092         // assume that we have an element reference instead of an id if the
15093         // parameter is not a string
15094         if (typeof id !== "string") {
15095             id = Roo.id(id);
15096         }
15097
15098         // set the id
15099         this.id = id;
15100
15101         // add to an interaction group
15102         this.addToGroup((sGroup) ? sGroup : "default");
15103
15104         // We don't want to register this as the handle with the manager
15105         // so we just set the id rather than calling the setter.
15106         this.handleElId = id;
15107
15108         // the linked element is the element that gets dragged by default
15109         this.setDragElId(id);
15110
15111         // by default, clicked anchors will not start drag operations.
15112         this.invalidHandleTypes = { A: "A" };
15113         this.invalidHandleIds = {};
15114         this.invalidHandleClasses = [];
15115
15116         this.applyConfig();
15117
15118         this.handleOnAvailable();
15119     },
15120
15121     /**
15122      * Applies the configuration parameters that were passed into the constructor.
15123      * This is supposed to happen at each level through the inheritance chain.  So
15124      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15125      * DragDrop in order to get all of the parameters that are available in
15126      * each object.
15127      * @method applyConfig
15128      */
15129     applyConfig: function() {
15130
15131         // configurable properties:
15132         //    padding, isTarget, maintainOffset, primaryButtonOnly
15133         this.padding           = this.config.padding || [0, 0, 0, 0];
15134         this.isTarget          = (this.config.isTarget !== false);
15135         this.maintainOffset    = (this.config.maintainOffset);
15136         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15137
15138     },
15139
15140     /**
15141      * Executed when the linked element is available
15142      * @method handleOnAvailable
15143      * @private
15144      */
15145     handleOnAvailable: function() {
15146         this.available = true;
15147         this.resetConstraints();
15148         this.onAvailable();
15149     },
15150
15151      /**
15152      * Configures the padding for the target zone in px.  Effectively expands
15153      * (or reduces) the virtual object size for targeting calculations.
15154      * Supports css-style shorthand; if only one parameter is passed, all sides
15155      * will have that padding, and if only two are passed, the top and bottom
15156      * will have the first param, the left and right the second.
15157      * @method setPadding
15158      * @param {int} iTop    Top pad
15159      * @param {int} iRight  Right pad
15160      * @param {int} iBot    Bot pad
15161      * @param {int} iLeft   Left pad
15162      */
15163     setPadding: function(iTop, iRight, iBot, iLeft) {
15164         // this.padding = [iLeft, iRight, iTop, iBot];
15165         if (!iRight && 0 !== iRight) {
15166             this.padding = [iTop, iTop, iTop, iTop];
15167         } else if (!iBot && 0 !== iBot) {
15168             this.padding = [iTop, iRight, iTop, iRight];
15169         } else {
15170             this.padding = [iTop, iRight, iBot, iLeft];
15171         }
15172     },
15173
15174     /**
15175      * Stores the initial placement of the linked element.
15176      * @method setInitialPosition
15177      * @param {int} diffX   the X offset, default 0
15178      * @param {int} diffY   the Y offset, default 0
15179      */
15180     setInitPosition: function(diffX, diffY) {
15181         var el = this.getEl();
15182
15183         if (!this.DDM.verifyEl(el)) {
15184             return;
15185         }
15186
15187         var dx = diffX || 0;
15188         var dy = diffY || 0;
15189
15190         var p = Dom.getXY( el );
15191
15192         this.initPageX = p[0] - dx;
15193         this.initPageY = p[1] - dy;
15194
15195         this.lastPageX = p[0];
15196         this.lastPageY = p[1];
15197
15198
15199         this.setStartPosition(p);
15200     },
15201
15202     /**
15203      * Sets the start position of the element.  This is set when the obj
15204      * is initialized, the reset when a drag is started.
15205      * @method setStartPosition
15206      * @param pos current position (from previous lookup)
15207      * @private
15208      */
15209     setStartPosition: function(pos) {
15210         var p = pos || Dom.getXY( this.getEl() );
15211         this.deltaSetXY = null;
15212
15213         this.startPageX = p[0];
15214         this.startPageY = p[1];
15215     },
15216
15217     /**
15218      * Add this instance to a group of related drag/drop objects.  All
15219      * instances belong to at least one group, and can belong to as many
15220      * groups as needed.
15221      * @method addToGroup
15222      * @param sGroup {string} the name of the group
15223      */
15224     addToGroup: function(sGroup) {
15225         this.groups[sGroup] = true;
15226         this.DDM.regDragDrop(this, sGroup);
15227     },
15228
15229     /**
15230      * Remove's this instance from the supplied interaction group
15231      * @method removeFromGroup
15232      * @param {string}  sGroup  The group to drop
15233      */
15234     removeFromGroup: function(sGroup) {
15235         if (this.groups[sGroup]) {
15236             delete this.groups[sGroup];
15237         }
15238
15239         this.DDM.removeDDFromGroup(this, sGroup);
15240     },
15241
15242     /**
15243      * Allows you to specify that an element other than the linked element
15244      * will be moved with the cursor during a drag
15245      * @method setDragElId
15246      * @param id {string} the id of the element that will be used to initiate the drag
15247      */
15248     setDragElId: function(id) {
15249         this.dragElId = id;
15250     },
15251
15252     /**
15253      * Allows you to specify a child of the linked element that should be
15254      * used to initiate the drag operation.  An example of this would be if
15255      * you have a content div with text and links.  Clicking anywhere in the
15256      * content area would normally start the drag operation.  Use this method
15257      * to specify that an element inside of the content div is the element
15258      * that starts the drag operation.
15259      * @method setHandleElId
15260      * @param id {string} the id of the element that will be used to
15261      * initiate the drag.
15262      */
15263     setHandleElId: function(id) {
15264         if (typeof id !== "string") {
15265             id = Roo.id(id);
15266         }
15267         this.handleElId = id;
15268         this.DDM.regHandle(this.id, id);
15269     },
15270
15271     /**
15272      * Allows you to set an element outside of the linked element as a drag
15273      * handle
15274      * @method setOuterHandleElId
15275      * @param id the id of the element that will be used to initiate the drag
15276      */
15277     setOuterHandleElId: function(id) {
15278         if (typeof id !== "string") {
15279             id = Roo.id(id);
15280         }
15281         Event.on(id, "mousedown",
15282                 this.handleMouseDown, this);
15283         this.setHandleElId(id);
15284
15285         this.hasOuterHandles = true;
15286     },
15287
15288     /**
15289      * Remove all drag and drop hooks for this element
15290      * @method unreg
15291      */
15292     unreg: function() {
15293         Event.un(this.id, "mousedown",
15294                 this.handleMouseDown);
15295         this._domRef = null;
15296         this.DDM._remove(this);
15297     },
15298
15299     destroy : function(){
15300         this.unreg();
15301     },
15302
15303     /**
15304      * Returns true if this instance is locked, or the drag drop mgr is locked
15305      * (meaning that all drag/drop is disabled on the page.)
15306      * @method isLocked
15307      * @return {boolean} true if this obj or all drag/drop is locked, else
15308      * false
15309      */
15310     isLocked: function() {
15311         return (this.DDM.isLocked() || this.locked);
15312     },
15313
15314     /**
15315      * Fired when this object is clicked
15316      * @method handleMouseDown
15317      * @param {Event} e
15318      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15319      * @private
15320      */
15321     handleMouseDown: function(e, oDD){
15322         if (this.primaryButtonOnly && e.button != 0) {
15323             return;
15324         }
15325
15326         if (this.isLocked()) {
15327             return;
15328         }
15329
15330         this.DDM.refreshCache(this.groups);
15331
15332         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15333         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15334         } else {
15335             if (this.clickValidator(e)) {
15336
15337                 // set the initial element position
15338                 this.setStartPosition();
15339
15340
15341                 this.b4MouseDown(e);
15342                 this.onMouseDown(e);
15343
15344                 this.DDM.handleMouseDown(e, this);
15345
15346                 this.DDM.stopEvent(e);
15347             } else {
15348
15349
15350             }
15351         }
15352     },
15353
15354     clickValidator: function(e) {
15355         var target = e.getTarget();
15356         return ( this.isValidHandleChild(target) &&
15357                     (this.id == this.handleElId ||
15358                         this.DDM.handleWasClicked(target, this.id)) );
15359     },
15360
15361     /**
15362      * Allows you to specify a tag name that should not start a drag operation
15363      * when clicked.  This is designed to facilitate embedding links within a
15364      * drag handle that do something other than start the drag.
15365      * @method addInvalidHandleType
15366      * @param {string} tagName the type of element to exclude
15367      */
15368     addInvalidHandleType: function(tagName) {
15369         var type = tagName.toUpperCase();
15370         this.invalidHandleTypes[type] = type;
15371     },
15372
15373     /**
15374      * Lets you to specify an element id for a child of a drag handle
15375      * that should not initiate a drag
15376      * @method addInvalidHandleId
15377      * @param {string} id the element id of the element you wish to ignore
15378      */
15379     addInvalidHandleId: function(id) {
15380         if (typeof id !== "string") {
15381             id = Roo.id(id);
15382         }
15383         this.invalidHandleIds[id] = id;
15384     },
15385
15386     /**
15387      * Lets you specify a css class of elements that will not initiate a drag
15388      * @method addInvalidHandleClass
15389      * @param {string} cssClass the class of the elements you wish to ignore
15390      */
15391     addInvalidHandleClass: function(cssClass) {
15392         this.invalidHandleClasses.push(cssClass);
15393     },
15394
15395     /**
15396      * Unsets an excluded tag name set by addInvalidHandleType
15397      * @method removeInvalidHandleType
15398      * @param {string} tagName the type of element to unexclude
15399      */
15400     removeInvalidHandleType: function(tagName) {
15401         var type = tagName.toUpperCase();
15402         // this.invalidHandleTypes[type] = null;
15403         delete this.invalidHandleTypes[type];
15404     },
15405
15406     /**
15407      * Unsets an invalid handle id
15408      * @method removeInvalidHandleId
15409      * @param {string} id the id of the element to re-enable
15410      */
15411     removeInvalidHandleId: function(id) {
15412         if (typeof id !== "string") {
15413             id = Roo.id(id);
15414         }
15415         delete this.invalidHandleIds[id];
15416     },
15417
15418     /**
15419      * Unsets an invalid css class
15420      * @method removeInvalidHandleClass
15421      * @param {string} cssClass the class of the element(s) you wish to
15422      * re-enable
15423      */
15424     removeInvalidHandleClass: function(cssClass) {
15425         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15426             if (this.invalidHandleClasses[i] == cssClass) {
15427                 delete this.invalidHandleClasses[i];
15428             }
15429         }
15430     },
15431
15432     /**
15433      * Checks the tag exclusion list to see if this click should be ignored
15434      * @method isValidHandleChild
15435      * @param {HTMLElement} node the HTMLElement to evaluate
15436      * @return {boolean} true if this is a valid tag type, false if not
15437      */
15438     isValidHandleChild: function(node) {
15439
15440         var valid = true;
15441         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15442         var nodeName;
15443         try {
15444             nodeName = node.nodeName.toUpperCase();
15445         } catch(e) {
15446             nodeName = node.nodeName;
15447         }
15448         valid = valid && !this.invalidHandleTypes[nodeName];
15449         valid = valid && !this.invalidHandleIds[node.id];
15450
15451         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15452             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15453         }
15454
15455
15456         return valid;
15457
15458     },
15459
15460     /**
15461      * Create the array of horizontal tick marks if an interval was specified
15462      * in setXConstraint().
15463      * @method setXTicks
15464      * @private
15465      */
15466     setXTicks: function(iStartX, iTickSize) {
15467         this.xTicks = [];
15468         this.xTickSize = iTickSize;
15469
15470         var tickMap = {};
15471
15472         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15473             if (!tickMap[i]) {
15474                 this.xTicks[this.xTicks.length] = i;
15475                 tickMap[i] = true;
15476             }
15477         }
15478
15479         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15480             if (!tickMap[i]) {
15481                 this.xTicks[this.xTicks.length] = i;
15482                 tickMap[i] = true;
15483             }
15484         }
15485
15486         this.xTicks.sort(this.DDM.numericSort) ;
15487     },
15488
15489     /**
15490      * Create the array of vertical tick marks if an interval was specified in
15491      * setYConstraint().
15492      * @method setYTicks
15493      * @private
15494      */
15495     setYTicks: function(iStartY, iTickSize) {
15496         this.yTicks = [];
15497         this.yTickSize = iTickSize;
15498
15499         var tickMap = {};
15500
15501         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15502             if (!tickMap[i]) {
15503                 this.yTicks[this.yTicks.length] = i;
15504                 tickMap[i] = true;
15505             }
15506         }
15507
15508         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15509             if (!tickMap[i]) {
15510                 this.yTicks[this.yTicks.length] = i;
15511                 tickMap[i] = true;
15512             }
15513         }
15514
15515         this.yTicks.sort(this.DDM.numericSort) ;
15516     },
15517
15518     /**
15519      * By default, the element can be dragged any place on the screen.  Use
15520      * this method to limit the horizontal travel of the element.  Pass in
15521      * 0,0 for the parameters if you want to lock the drag to the y axis.
15522      * @method setXConstraint
15523      * @param {int} iLeft the number of pixels the element can move to the left
15524      * @param {int} iRight the number of pixels the element can move to the
15525      * right
15526      * @param {int} iTickSize optional parameter for specifying that the
15527      * element
15528      * should move iTickSize pixels at a time.
15529      */
15530     setXConstraint: function(iLeft, iRight, iTickSize) {
15531         this.leftConstraint = iLeft;
15532         this.rightConstraint = iRight;
15533
15534         this.minX = this.initPageX - iLeft;
15535         this.maxX = this.initPageX + iRight;
15536         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15537
15538         this.constrainX = true;
15539     },
15540
15541     /**
15542      * Clears any constraints applied to this instance.  Also clears ticks
15543      * since they can't exist independent of a constraint at this time.
15544      * @method clearConstraints
15545      */
15546     clearConstraints: function() {
15547         this.constrainX = false;
15548         this.constrainY = false;
15549         this.clearTicks();
15550     },
15551
15552     /**
15553      * Clears any tick interval defined for this instance
15554      * @method clearTicks
15555      */
15556     clearTicks: function() {
15557         this.xTicks = null;
15558         this.yTicks = null;
15559         this.xTickSize = 0;
15560         this.yTickSize = 0;
15561     },
15562
15563     /**
15564      * By default, the element can be dragged any place on the screen.  Set
15565      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15566      * parameters if you want to lock the drag to the x axis.
15567      * @method setYConstraint
15568      * @param {int} iUp the number of pixels the element can move up
15569      * @param {int} iDown the number of pixels the element can move down
15570      * @param {int} iTickSize optional parameter for specifying that the
15571      * element should move iTickSize pixels at a time.
15572      */
15573     setYConstraint: function(iUp, iDown, iTickSize) {
15574         this.topConstraint = iUp;
15575         this.bottomConstraint = iDown;
15576
15577         this.minY = this.initPageY - iUp;
15578         this.maxY = this.initPageY + iDown;
15579         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15580
15581         this.constrainY = true;
15582
15583     },
15584
15585     /**
15586      * resetConstraints must be called if you manually reposition a dd element.
15587      * @method resetConstraints
15588      * @param {boolean} maintainOffset
15589      */
15590     resetConstraints: function() {
15591
15592
15593         // Maintain offsets if necessary
15594         if (this.initPageX || this.initPageX === 0) {
15595             // figure out how much this thing has moved
15596             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15597             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15598
15599             this.setInitPosition(dx, dy);
15600
15601         // This is the first time we have detected the element's position
15602         } else {
15603             this.setInitPosition();
15604         }
15605
15606         if (this.constrainX) {
15607             this.setXConstraint( this.leftConstraint,
15608                                  this.rightConstraint,
15609                                  this.xTickSize        );
15610         }
15611
15612         if (this.constrainY) {
15613             this.setYConstraint( this.topConstraint,
15614                                  this.bottomConstraint,
15615                                  this.yTickSize         );
15616         }
15617     },
15618
15619     /**
15620      * Normally the drag element is moved pixel by pixel, but we can specify
15621      * that it move a number of pixels at a time.  This method resolves the
15622      * location when we have it set up like this.
15623      * @method getTick
15624      * @param {int} val where we want to place the object
15625      * @param {int[]} tickArray sorted array of valid points
15626      * @return {int} the closest tick
15627      * @private
15628      */
15629     getTick: function(val, tickArray) {
15630
15631         if (!tickArray) {
15632             // If tick interval is not defined, it is effectively 1 pixel,
15633             // so we return the value passed to us.
15634             return val;
15635         } else if (tickArray[0] >= val) {
15636             // The value is lower than the first tick, so we return the first
15637             // tick.
15638             return tickArray[0];
15639         } else {
15640             for (var i=0, len=tickArray.length; i<len; ++i) {
15641                 var next = i + 1;
15642                 if (tickArray[next] && tickArray[next] >= val) {
15643                     var diff1 = val - tickArray[i];
15644                     var diff2 = tickArray[next] - val;
15645                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15646                 }
15647             }
15648
15649             // The value is larger than the last tick, so we return the last
15650             // tick.
15651             return tickArray[tickArray.length - 1];
15652         }
15653     },
15654
15655     /**
15656      * toString method
15657      * @method toString
15658      * @return {string} string representation of the dd obj
15659      */
15660     toString: function() {
15661         return ("DragDrop " + this.id);
15662     }
15663
15664 });
15665
15666 })();
15667 /*
15668  * Based on:
15669  * Ext JS Library 1.1.1
15670  * Copyright(c) 2006-2007, Ext JS, LLC.
15671  *
15672  * Originally Released Under LGPL - original licence link has changed is not relivant.
15673  *
15674  * Fork - LGPL
15675  * <script type="text/javascript">
15676  */
15677
15678
15679 /**
15680  * The drag and drop utility provides a framework for building drag and drop
15681  * applications.  In addition to enabling drag and drop for specific elements,
15682  * the drag and drop elements are tracked by the manager class, and the
15683  * interactions between the various elements are tracked during the drag and
15684  * the implementing code is notified about these important moments.
15685  */
15686
15687 // Only load the library once.  Rewriting the manager class would orphan
15688 // existing drag and drop instances.
15689 if (!Roo.dd.DragDropMgr) {
15690
15691 /**
15692  * @class Roo.dd.DragDropMgr
15693  * DragDropMgr is a singleton that tracks the element interaction for
15694  * all DragDrop items in the window.  Generally, you will not call
15695  * this class directly, but it does have helper methods that could
15696  * be useful in your DragDrop implementations.
15697  * @singleton
15698  */
15699 Roo.dd.DragDropMgr = function() {
15700
15701     var Event = Roo.EventManager;
15702
15703     return {
15704
15705         /**
15706          * Two dimensional Array of registered DragDrop objects.  The first
15707          * dimension is the DragDrop item group, the second the DragDrop
15708          * object.
15709          * @property ids
15710          * @type {string: string}
15711          * @private
15712          * @static
15713          */
15714         ids: {},
15715
15716         /**
15717          * Array of element ids defined as drag handles.  Used to determine
15718          * if the element that generated the mousedown event is actually the
15719          * handle and not the html element itself.
15720          * @property handleIds
15721          * @type {string: string}
15722          * @private
15723          * @static
15724          */
15725         handleIds: {},
15726
15727         /**
15728          * the DragDrop object that is currently being dragged
15729          * @property dragCurrent
15730          * @type DragDrop
15731          * @private
15732          * @static
15733          **/
15734         dragCurrent: null,
15735
15736         /**
15737          * the DragDrop object(s) that are being hovered over
15738          * @property dragOvers
15739          * @type Array
15740          * @private
15741          * @static
15742          */
15743         dragOvers: {},
15744
15745         /**
15746          * the X distance between the cursor and the object being dragged
15747          * @property deltaX
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaX: 0,
15753
15754         /**
15755          * the Y distance between the cursor and the object being dragged
15756          * @property deltaY
15757          * @type int
15758          * @private
15759          * @static
15760          */
15761         deltaY: 0,
15762
15763         /**
15764          * Flag to determine if we should prevent the default behavior of the
15765          * events we define. By default this is true, but this can be set to
15766          * false if you need the default behavior (not recommended)
15767          * @property preventDefault
15768          * @type boolean
15769          * @static
15770          */
15771         preventDefault: true,
15772
15773         /**
15774          * Flag to determine if we should stop the propagation of the events
15775          * we generate. This is true by default but you may want to set it to
15776          * false if the html element contains other features that require the
15777          * mouse click.
15778          * @property stopPropagation
15779          * @type boolean
15780          * @static
15781          */
15782         stopPropagation: true,
15783
15784         /**
15785          * Internal flag that is set to true when drag and drop has been
15786          * intialized
15787          * @property initialized
15788          * @private
15789          * @static
15790          */
15791         initalized: false,
15792
15793         /**
15794          * All drag and drop can be disabled.
15795          * @property locked
15796          * @private
15797          * @static
15798          */
15799         locked: false,
15800
15801         /**
15802          * Called the first time an element is registered.
15803          * @method init
15804          * @private
15805          * @static
15806          */
15807         init: function() {
15808             this.initialized = true;
15809         },
15810
15811         /**
15812          * In point mode, drag and drop interaction is defined by the
15813          * location of the cursor during the drag/drop
15814          * @property POINT
15815          * @type int
15816          * @static
15817          */
15818         POINT: 0,
15819
15820         /**
15821          * In intersect mode, drag and drop interactio nis defined by the
15822          * overlap of two or more drag and drop objects.
15823          * @property INTERSECT
15824          * @type int
15825          * @static
15826          */
15827         INTERSECT: 1,
15828
15829         /**
15830          * The current drag and drop mode.  Default: POINT
15831          * @property mode
15832          * @type int
15833          * @static
15834          */
15835         mode: 0,
15836
15837         /**
15838          * Runs method on all drag and drop objects
15839          * @method _execOnAll
15840          * @private
15841          * @static
15842          */
15843         _execOnAll: function(sMethod, args) {
15844             for (var i in this.ids) {
15845                 for (var j in this.ids[i]) {
15846                     var oDD = this.ids[i][j];
15847                     if (! this.isTypeOfDD(oDD)) {
15848                         continue;
15849                     }
15850                     oDD[sMethod].apply(oDD, args);
15851                 }
15852             }
15853         },
15854
15855         /**
15856          * Drag and drop initialization.  Sets up the global event handlers
15857          * @method _onLoad
15858          * @private
15859          * @static
15860          */
15861         _onLoad: function() {
15862
15863             this.init();
15864
15865
15866             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15867             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15868             Event.on(window,   "unload",    this._onUnload, this, true);
15869             Event.on(window,   "resize",    this._onResize, this, true);
15870             // Event.on(window,   "mouseout",    this._test);
15871
15872         },
15873
15874         /**
15875          * Reset constraints on all drag and drop objs
15876          * @method _onResize
15877          * @private
15878          * @static
15879          */
15880         _onResize: function(e) {
15881             this._execOnAll("resetConstraints", []);
15882         },
15883
15884         /**
15885          * Lock all drag and drop functionality
15886          * @method lock
15887          * @static
15888          */
15889         lock: function() { this.locked = true; },
15890
15891         /**
15892          * Unlock all drag and drop functionality
15893          * @method unlock
15894          * @static
15895          */
15896         unlock: function() { this.locked = false; },
15897
15898         /**
15899          * Is drag and drop locked?
15900          * @method isLocked
15901          * @return {boolean} True if drag and drop is locked, false otherwise.
15902          * @static
15903          */
15904         isLocked: function() { return this.locked; },
15905
15906         /**
15907          * Location cache that is set for all drag drop objects when a drag is
15908          * initiated, cleared when the drag is finished.
15909          * @property locationCache
15910          * @private
15911          * @static
15912          */
15913         locationCache: {},
15914
15915         /**
15916          * Set useCache to false if you want to force object the lookup of each
15917          * drag and drop linked element constantly during a drag.
15918          * @property useCache
15919          * @type boolean
15920          * @static
15921          */
15922         useCache: true,
15923
15924         /**
15925          * The number of pixels that the mouse needs to move after the
15926          * mousedown before the drag is initiated.  Default=3;
15927          * @property clickPixelThresh
15928          * @type int
15929          * @static
15930          */
15931         clickPixelThresh: 3,
15932
15933         /**
15934          * The number of milliseconds after the mousedown event to initiate the
15935          * drag if we don't get a mouseup event. Default=1000
15936          * @property clickTimeThresh
15937          * @type int
15938          * @static
15939          */
15940         clickTimeThresh: 350,
15941
15942         /**
15943          * Flag that indicates that either the drag pixel threshold or the
15944          * mousdown time threshold has been met
15945          * @property dragThreshMet
15946          * @type boolean
15947          * @private
15948          * @static
15949          */
15950         dragThreshMet: false,
15951
15952         /**
15953          * Timeout used for the click time threshold
15954          * @property clickTimeout
15955          * @type Object
15956          * @private
15957          * @static
15958          */
15959         clickTimeout: null,
15960
15961         /**
15962          * The X position of the mousedown event stored for later use when a
15963          * drag threshold is met.
15964          * @property startX
15965          * @type int
15966          * @private
15967          * @static
15968          */
15969         startX: 0,
15970
15971         /**
15972          * The Y position of the mousedown event stored for later use when a
15973          * drag threshold is met.
15974          * @property startY
15975          * @type int
15976          * @private
15977          * @static
15978          */
15979         startY: 0,
15980
15981         /**
15982          * Each DragDrop instance must be registered with the DragDropMgr.
15983          * This is executed in DragDrop.init()
15984          * @method regDragDrop
15985          * @param {DragDrop} oDD the DragDrop object to register
15986          * @param {String} sGroup the name of the group this element belongs to
15987          * @static
15988          */
15989         regDragDrop: function(oDD, sGroup) {
15990             if (!this.initialized) { this.init(); }
15991
15992             if (!this.ids[sGroup]) {
15993                 this.ids[sGroup] = {};
15994             }
15995             this.ids[sGroup][oDD.id] = oDD;
15996         },
15997
15998         /**
15999          * Removes the supplied dd instance from the supplied group. Executed
16000          * by DragDrop.removeFromGroup, so don't call this function directly.
16001          * @method removeDDFromGroup
16002          * @private
16003          * @static
16004          */
16005         removeDDFromGroup: function(oDD, sGroup) {
16006             if (!this.ids[sGroup]) {
16007                 this.ids[sGroup] = {};
16008             }
16009
16010             var obj = this.ids[sGroup];
16011             if (obj && obj[oDD.id]) {
16012                 delete obj[oDD.id];
16013             }
16014         },
16015
16016         /**
16017          * Unregisters a drag and drop item.  This is executed in
16018          * DragDrop.unreg, use that method instead of calling this directly.
16019          * @method _remove
16020          * @private
16021          * @static
16022          */
16023         _remove: function(oDD) {
16024             for (var g in oDD.groups) {
16025                 if (g && this.ids[g][oDD.id]) {
16026                     delete this.ids[g][oDD.id];
16027                 }
16028             }
16029             delete this.handleIds[oDD.id];
16030         },
16031
16032         /**
16033          * Each DragDrop handle element must be registered.  This is done
16034          * automatically when executing DragDrop.setHandleElId()
16035          * @method regHandle
16036          * @param {String} sDDId the DragDrop id this element is a handle for
16037          * @param {String} sHandleId the id of the element that is the drag
16038          * handle
16039          * @static
16040          */
16041         regHandle: function(sDDId, sHandleId) {
16042             if (!this.handleIds[sDDId]) {
16043                 this.handleIds[sDDId] = {};
16044             }
16045             this.handleIds[sDDId][sHandleId] = sHandleId;
16046         },
16047
16048         /**
16049          * Utility function to determine if a given element has been
16050          * registered as a drag drop item.
16051          * @method isDragDrop
16052          * @param {String} id the element id to check
16053          * @return {boolean} true if this element is a DragDrop item,
16054          * false otherwise
16055          * @static
16056          */
16057         isDragDrop: function(id) {
16058             return ( this.getDDById(id) ) ? true : false;
16059         },
16060
16061         /**
16062          * Returns the drag and drop instances that are in all groups the
16063          * passed in instance belongs to.
16064          * @method getRelated
16065          * @param {DragDrop} p_oDD the obj to get related data for
16066          * @param {boolean} bTargetsOnly if true, only return targetable objs
16067          * @return {DragDrop[]} the related instances
16068          * @static
16069          */
16070         getRelated: function(p_oDD, bTargetsOnly) {
16071             var oDDs = [];
16072             for (var i in p_oDD.groups) {
16073                 for (j in this.ids[i]) {
16074                     var dd = this.ids[i][j];
16075                     if (! this.isTypeOfDD(dd)) {
16076                         continue;
16077                     }
16078                     if (!bTargetsOnly || dd.isTarget) {
16079                         oDDs[oDDs.length] = dd;
16080                     }
16081                 }
16082             }
16083
16084             return oDDs;
16085         },
16086
16087         /**
16088          * Returns true if the specified dd target is a legal target for
16089          * the specifice drag obj
16090          * @method isLegalTarget
16091          * @param {DragDrop} the drag obj
16092          * @param {DragDrop} the target
16093          * @return {boolean} true if the target is a legal target for the
16094          * dd obj
16095          * @static
16096          */
16097         isLegalTarget: function (oDD, oTargetDD) {
16098             var targets = this.getRelated(oDD, true);
16099             for (var i=0, len=targets.length;i<len;++i) {
16100                 if (targets[i].id == oTargetDD.id) {
16101                     return true;
16102                 }
16103             }
16104
16105             return false;
16106         },
16107
16108         /**
16109          * My goal is to be able to transparently determine if an object is
16110          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16111          * returns "object", oDD.constructor.toString() always returns
16112          * "DragDrop" and not the name of the subclass.  So for now it just
16113          * evaluates a well-known variable in DragDrop.
16114          * @method isTypeOfDD
16115          * @param {Object} the object to evaluate
16116          * @return {boolean} true if typeof oDD = DragDrop
16117          * @static
16118          */
16119         isTypeOfDD: function (oDD) {
16120             return (oDD && oDD.__ygDragDrop);
16121         },
16122
16123         /**
16124          * Utility function to determine if a given element has been
16125          * registered as a drag drop handle for the given Drag Drop object.
16126          * @method isHandle
16127          * @param {String} id the element id to check
16128          * @return {boolean} true if this element is a DragDrop handle, false
16129          * otherwise
16130          * @static
16131          */
16132         isHandle: function(sDDId, sHandleId) {
16133             return ( this.handleIds[sDDId] &&
16134                             this.handleIds[sDDId][sHandleId] );
16135         },
16136
16137         /**
16138          * Returns the DragDrop instance for a given id
16139          * @method getDDById
16140          * @param {String} id the id of the DragDrop object
16141          * @return {DragDrop} the drag drop object, null if it is not found
16142          * @static
16143          */
16144         getDDById: function(id) {
16145             for (var i in this.ids) {
16146                 if (this.ids[i][id]) {
16147                     return this.ids[i][id];
16148                 }
16149             }
16150             return null;
16151         },
16152
16153         /**
16154          * Fired after a registered DragDrop object gets the mousedown event.
16155          * Sets up the events required to track the object being dragged
16156          * @method handleMouseDown
16157          * @param {Event} e the event
16158          * @param oDD the DragDrop object being dragged
16159          * @private
16160          * @static
16161          */
16162         handleMouseDown: function(e, oDD) {
16163             if(Roo.QuickTips){
16164                 Roo.QuickTips.disable();
16165             }
16166             this.currentTarget = e.getTarget();
16167
16168             this.dragCurrent = oDD;
16169
16170             var el = oDD.getEl();
16171
16172             // track start position
16173             this.startX = e.getPageX();
16174             this.startY = e.getPageY();
16175
16176             this.deltaX = this.startX - el.offsetLeft;
16177             this.deltaY = this.startY - el.offsetTop;
16178
16179             this.dragThreshMet = false;
16180
16181             this.clickTimeout = setTimeout(
16182                     function() {
16183                         var DDM = Roo.dd.DDM;
16184                         DDM.startDrag(DDM.startX, DDM.startY);
16185                     },
16186                     this.clickTimeThresh );
16187         },
16188
16189         /**
16190          * Fired when either the drag pixel threshol or the mousedown hold
16191          * time threshold has been met.
16192          * @method startDrag
16193          * @param x {int} the X position of the original mousedown
16194          * @param y {int} the Y position of the original mousedown
16195          * @static
16196          */
16197         startDrag: function(x, y) {
16198             clearTimeout(this.clickTimeout);
16199             if (this.dragCurrent) {
16200                 this.dragCurrent.b4StartDrag(x, y);
16201                 this.dragCurrent.startDrag(x, y);
16202             }
16203             this.dragThreshMet = true;
16204         },
16205
16206         /**
16207          * Internal function to handle the mouseup event.  Will be invoked
16208          * from the context of the document.
16209          * @method handleMouseUp
16210          * @param {Event} e the event
16211          * @private
16212          * @static
16213          */
16214         handleMouseUp: function(e) {
16215
16216             if(Roo.QuickTips){
16217                 Roo.QuickTips.enable();
16218             }
16219             if (! this.dragCurrent) {
16220                 return;
16221             }
16222
16223             clearTimeout(this.clickTimeout);
16224
16225             if (this.dragThreshMet) {
16226                 this.fireEvents(e, true);
16227             } else {
16228             }
16229
16230             this.stopDrag(e);
16231
16232             this.stopEvent(e);
16233         },
16234
16235         /**
16236          * Utility to stop event propagation and event default, if these
16237          * features are turned on.
16238          * @method stopEvent
16239          * @param {Event} e the event as returned by this.getEvent()
16240          * @static
16241          */
16242         stopEvent: function(e){
16243             if(this.stopPropagation) {
16244                 e.stopPropagation();
16245             }
16246
16247             if (this.preventDefault) {
16248                 e.preventDefault();
16249             }
16250         },
16251
16252         /**
16253          * Internal function to clean up event handlers after the drag
16254          * operation is complete
16255          * @method stopDrag
16256          * @param {Event} e the event
16257          * @private
16258          * @static
16259          */
16260         stopDrag: function(e) {
16261             // Fire the drag end event for the item that was dragged
16262             if (this.dragCurrent) {
16263                 if (this.dragThreshMet) {
16264                     this.dragCurrent.b4EndDrag(e);
16265                     this.dragCurrent.endDrag(e);
16266                 }
16267
16268                 this.dragCurrent.onMouseUp(e);
16269             }
16270
16271             this.dragCurrent = null;
16272             this.dragOvers = {};
16273         },
16274
16275         /**
16276          * Internal function to handle the mousemove event.  Will be invoked
16277          * from the context of the html element.
16278          *
16279          * @TODO figure out what we can do about mouse events lost when the
16280          * user drags objects beyond the window boundary.  Currently we can
16281          * detect this in internet explorer by verifying that the mouse is
16282          * down during the mousemove event.  Firefox doesn't give us the
16283          * button state on the mousemove event.
16284          * @method handleMouseMove
16285          * @param {Event} e the event
16286          * @private
16287          * @static
16288          */
16289         handleMouseMove: function(e) {
16290             if (! this.dragCurrent) {
16291                 return true;
16292             }
16293
16294             // var button = e.which || e.button;
16295
16296             // check for IE mouseup outside of page boundary
16297             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16298                 this.stopEvent(e);
16299                 return this.handleMouseUp(e);
16300             }
16301
16302             if (!this.dragThreshMet) {
16303                 var diffX = Math.abs(this.startX - e.getPageX());
16304                 var diffY = Math.abs(this.startY - e.getPageY());
16305                 if (diffX > this.clickPixelThresh ||
16306                             diffY > this.clickPixelThresh) {
16307                     this.startDrag(this.startX, this.startY);
16308                 }
16309             }
16310
16311             if (this.dragThreshMet) {
16312                 this.dragCurrent.b4Drag(e);
16313                 this.dragCurrent.onDrag(e);
16314                 if(!this.dragCurrent.moveOnly){
16315                     this.fireEvents(e, false);
16316                 }
16317             }
16318
16319             this.stopEvent(e);
16320
16321             return true;
16322         },
16323
16324         /**
16325          * Iterates over all of the DragDrop elements to find ones we are
16326          * hovering over or dropping on
16327          * @method fireEvents
16328          * @param {Event} e the event
16329          * @param {boolean} isDrop is this a drop op or a mouseover op?
16330          * @private
16331          * @static
16332          */
16333         fireEvents: function(e, isDrop) {
16334             var dc = this.dragCurrent;
16335
16336             // If the user did the mouse up outside of the window, we could
16337             // get here even though we have ended the drag.
16338             if (!dc || dc.isLocked()) {
16339                 return;
16340             }
16341
16342             var pt = e.getPoint();
16343
16344             // cache the previous dragOver array
16345             var oldOvers = [];
16346
16347             var outEvts   = [];
16348             var overEvts  = [];
16349             var dropEvts  = [];
16350             var enterEvts = [];
16351
16352             // Check to see if the object(s) we were hovering over is no longer
16353             // being hovered over so we can fire the onDragOut event
16354             for (var i in this.dragOvers) {
16355
16356                 var ddo = this.dragOvers[i];
16357
16358                 if (! this.isTypeOfDD(ddo)) {
16359                     continue;
16360                 }
16361
16362                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16363                     outEvts.push( ddo );
16364                 }
16365
16366                 oldOvers[i] = true;
16367                 delete this.dragOvers[i];
16368             }
16369
16370             for (var sGroup in dc.groups) {
16371
16372                 if ("string" != typeof sGroup) {
16373                     continue;
16374                 }
16375
16376                 for (i in this.ids[sGroup]) {
16377                     var oDD = this.ids[sGroup][i];
16378                     if (! this.isTypeOfDD(oDD)) {
16379                         continue;
16380                     }
16381
16382                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16383                         if (this.isOverTarget(pt, oDD, this.mode)) {
16384                             // look for drop interactions
16385                             if (isDrop) {
16386                                 dropEvts.push( oDD );
16387                             // look for drag enter and drag over interactions
16388                             } else {
16389
16390                                 // initial drag over: dragEnter fires
16391                                 if (!oldOvers[oDD.id]) {
16392                                     enterEvts.push( oDD );
16393                                 // subsequent drag overs: dragOver fires
16394                                 } else {
16395                                     overEvts.push( oDD );
16396                                 }
16397
16398                                 this.dragOvers[oDD.id] = oDD;
16399                             }
16400                         }
16401                     }
16402                 }
16403             }
16404
16405             if (this.mode) {
16406                 if (outEvts.length) {
16407                     dc.b4DragOut(e, outEvts);
16408                     dc.onDragOut(e, outEvts);
16409                 }
16410
16411                 if (enterEvts.length) {
16412                     dc.onDragEnter(e, enterEvts);
16413                 }
16414
16415                 if (overEvts.length) {
16416                     dc.b4DragOver(e, overEvts);
16417                     dc.onDragOver(e, overEvts);
16418                 }
16419
16420                 if (dropEvts.length) {
16421                     dc.b4DragDrop(e, dropEvts);
16422                     dc.onDragDrop(e, dropEvts);
16423                 }
16424
16425             } else {
16426                 // fire dragout events
16427                 var len = 0;
16428                 for (i=0, len=outEvts.length; i<len; ++i) {
16429                     dc.b4DragOut(e, outEvts[i].id);
16430                     dc.onDragOut(e, outEvts[i].id);
16431                 }
16432
16433                 // fire enter events
16434                 for (i=0,len=enterEvts.length; i<len; ++i) {
16435                     // dc.b4DragEnter(e, oDD.id);
16436                     dc.onDragEnter(e, enterEvts[i].id);
16437                 }
16438
16439                 // fire over events
16440                 for (i=0,len=overEvts.length; i<len; ++i) {
16441                     dc.b4DragOver(e, overEvts[i].id);
16442                     dc.onDragOver(e, overEvts[i].id);
16443                 }
16444
16445                 // fire drop events
16446                 for (i=0, len=dropEvts.length; i<len; ++i) {
16447                     dc.b4DragDrop(e, dropEvts[i].id);
16448                     dc.onDragDrop(e, dropEvts[i].id);
16449                 }
16450
16451             }
16452
16453             // notify about a drop that did not find a target
16454             if (isDrop && !dropEvts.length) {
16455                 dc.onInvalidDrop(e);
16456             }
16457
16458         },
16459
16460         /**
16461          * Helper function for getting the best match from the list of drag
16462          * and drop objects returned by the drag and drop events when we are
16463          * in INTERSECT mode.  It returns either the first object that the
16464          * cursor is over, or the object that has the greatest overlap with
16465          * the dragged element.
16466          * @method getBestMatch
16467          * @param  {DragDrop[]} dds The array of drag and drop objects
16468          * targeted
16469          * @return {DragDrop}       The best single match
16470          * @static
16471          */
16472         getBestMatch: function(dds) {
16473             var winner = null;
16474             // Return null if the input is not what we expect
16475             //if (!dds || !dds.length || dds.length == 0) {
16476                // winner = null;
16477             // If there is only one item, it wins
16478             //} else if (dds.length == 1) {
16479
16480             var len = dds.length;
16481
16482             if (len == 1) {
16483                 winner = dds[0];
16484             } else {
16485                 // Loop through the targeted items
16486                 for (var i=0; i<len; ++i) {
16487                     var dd = dds[i];
16488                     // If the cursor is over the object, it wins.  If the
16489                     // cursor is over multiple matches, the first one we come
16490                     // to wins.
16491                     if (dd.cursorIsOver) {
16492                         winner = dd;
16493                         break;
16494                     // Otherwise the object with the most overlap wins
16495                     } else {
16496                         if (!winner ||
16497                             winner.overlap.getArea() < dd.overlap.getArea()) {
16498                             winner = dd;
16499                         }
16500                     }
16501                 }
16502             }
16503
16504             return winner;
16505         },
16506
16507         /**
16508          * Refreshes the cache of the top-left and bottom-right points of the
16509          * drag and drop objects in the specified group(s).  This is in the
16510          * format that is stored in the drag and drop instance, so typical
16511          * usage is:
16512          * <code>
16513          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16514          * </code>
16515          * Alternatively:
16516          * <code>
16517          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16518          * </code>
16519          * @TODO this really should be an indexed array.  Alternatively this
16520          * method could accept both.
16521          * @method refreshCache
16522          * @param {Object} groups an associative array of groups to refresh
16523          * @static
16524          */
16525         refreshCache: function(groups) {
16526             for (var sGroup in groups) {
16527                 if ("string" != typeof sGroup) {
16528                     continue;
16529                 }
16530                 for (var i in this.ids[sGroup]) {
16531                     var oDD = this.ids[sGroup][i];
16532
16533                     if (this.isTypeOfDD(oDD)) {
16534                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16535                         var loc = this.getLocation(oDD);
16536                         if (loc) {
16537                             this.locationCache[oDD.id] = loc;
16538                         } else {
16539                             delete this.locationCache[oDD.id];
16540                             // this will unregister the drag and drop object if
16541                             // the element is not in a usable state
16542                             // oDD.unreg();
16543                         }
16544                     }
16545                 }
16546             }
16547         },
16548
16549         /**
16550          * This checks to make sure an element exists and is in the DOM.  The
16551          * main purpose is to handle cases where innerHTML is used to remove
16552          * drag and drop objects from the DOM.  IE provides an 'unspecified
16553          * error' when trying to access the offsetParent of such an element
16554          * @method verifyEl
16555          * @param {HTMLElement} el the element to check
16556          * @return {boolean} true if the element looks usable
16557          * @static
16558          */
16559         verifyEl: function(el) {
16560             if (el) {
16561                 var parent;
16562                 if(Roo.isIE){
16563                     try{
16564                         parent = el.offsetParent;
16565                     }catch(e){}
16566                 }else{
16567                     parent = el.offsetParent;
16568                 }
16569                 if (parent) {
16570                     return true;
16571                 }
16572             }
16573
16574             return false;
16575         },
16576
16577         /**
16578          * Returns a Region object containing the drag and drop element's position
16579          * and size, including the padding configured for it
16580          * @method getLocation
16581          * @param {DragDrop} oDD the drag and drop object to get the
16582          *                       location for
16583          * @return {Roo.lib.Region} a Region object representing the total area
16584          *                             the element occupies, including any padding
16585          *                             the instance is configured for.
16586          * @static
16587          */
16588         getLocation: function(oDD) {
16589             if (! this.isTypeOfDD(oDD)) {
16590                 return null;
16591             }
16592
16593             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16594
16595             try {
16596                 pos= Roo.lib.Dom.getXY(el);
16597             } catch (e) { }
16598
16599             if (!pos) {
16600                 return null;
16601             }
16602
16603             x1 = pos[0];
16604             x2 = x1 + el.offsetWidth;
16605             y1 = pos[1];
16606             y2 = y1 + el.offsetHeight;
16607
16608             t = y1 - oDD.padding[0];
16609             r = x2 + oDD.padding[1];
16610             b = y2 + oDD.padding[2];
16611             l = x1 - oDD.padding[3];
16612
16613             return new Roo.lib.Region( t, r, b, l );
16614         },
16615
16616         /**
16617          * Checks the cursor location to see if it over the target
16618          * @method isOverTarget
16619          * @param {Roo.lib.Point} pt The point to evaluate
16620          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16621          * @return {boolean} true if the mouse is over the target
16622          * @private
16623          * @static
16624          */
16625         isOverTarget: function(pt, oTarget, intersect) {
16626             // use cache if available
16627             var loc = this.locationCache[oTarget.id];
16628             if (!loc || !this.useCache) {
16629                 loc = this.getLocation(oTarget);
16630                 this.locationCache[oTarget.id] = loc;
16631
16632             }
16633
16634             if (!loc) {
16635                 return false;
16636             }
16637
16638             oTarget.cursorIsOver = loc.contains( pt );
16639
16640             // DragDrop is using this as a sanity check for the initial mousedown
16641             // in this case we are done.  In POINT mode, if the drag obj has no
16642             // contraints, we are also done. Otherwise we need to evaluate the
16643             // location of the target as related to the actual location of the
16644             // dragged element.
16645             var dc = this.dragCurrent;
16646             if (!dc || !dc.getTargetCoord ||
16647                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16648                 return oTarget.cursorIsOver;
16649             }
16650
16651             oTarget.overlap = null;
16652
16653             // Get the current location of the drag element, this is the
16654             // location of the mouse event less the delta that represents
16655             // where the original mousedown happened on the element.  We
16656             // need to consider constraints and ticks as well.
16657             var pos = dc.getTargetCoord(pt.x, pt.y);
16658
16659             var el = dc.getDragEl();
16660             var curRegion = new Roo.lib.Region( pos.y,
16661                                                    pos.x + el.offsetWidth,
16662                                                    pos.y + el.offsetHeight,
16663                                                    pos.x );
16664
16665             var overlap = curRegion.intersect(loc);
16666
16667             if (overlap) {
16668                 oTarget.overlap = overlap;
16669                 return (intersect) ? true : oTarget.cursorIsOver;
16670             } else {
16671                 return false;
16672             }
16673         },
16674
16675         /**
16676          * unload event handler
16677          * @method _onUnload
16678          * @private
16679          * @static
16680          */
16681         _onUnload: function(e, me) {
16682             Roo.dd.DragDropMgr.unregAll();
16683         },
16684
16685         /**
16686          * Cleans up the drag and drop events and objects.
16687          * @method unregAll
16688          * @private
16689          * @static
16690          */
16691         unregAll: function() {
16692
16693             if (this.dragCurrent) {
16694                 this.stopDrag();
16695                 this.dragCurrent = null;
16696             }
16697
16698             this._execOnAll("unreg", []);
16699
16700             for (i in this.elementCache) {
16701                 delete this.elementCache[i];
16702             }
16703
16704             this.elementCache = {};
16705             this.ids = {};
16706         },
16707
16708         /**
16709          * A cache of DOM elements
16710          * @property elementCache
16711          * @private
16712          * @static
16713          */
16714         elementCache: {},
16715
16716         /**
16717          * Get the wrapper for the DOM element specified
16718          * @method getElWrapper
16719          * @param {String} id the id of the element to get
16720          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16721          * @private
16722          * @deprecated This wrapper isn't that useful
16723          * @static
16724          */
16725         getElWrapper: function(id) {
16726             var oWrapper = this.elementCache[id];
16727             if (!oWrapper || !oWrapper.el) {
16728                 oWrapper = this.elementCache[id] =
16729                     new this.ElementWrapper(Roo.getDom(id));
16730             }
16731             return oWrapper;
16732         },
16733
16734         /**
16735          * Returns the actual DOM element
16736          * @method getElement
16737          * @param {String} id the id of the elment to get
16738          * @return {Object} The element
16739          * @deprecated use Roo.getDom instead
16740          * @static
16741          */
16742         getElement: function(id) {
16743             return Roo.getDom(id);
16744         },
16745
16746         /**
16747          * Returns the style property for the DOM element (i.e.,
16748          * document.getElById(id).style)
16749          * @method getCss
16750          * @param {String} id the id of the elment to get
16751          * @return {Object} The style property of the element
16752          * @deprecated use Roo.getDom instead
16753          * @static
16754          */
16755         getCss: function(id) {
16756             var el = Roo.getDom(id);
16757             return (el) ? el.style : null;
16758         },
16759
16760         /**
16761          * Inner class for cached elements
16762          * @class DragDropMgr.ElementWrapper
16763          * @for DragDropMgr
16764          * @private
16765          * @deprecated
16766          */
16767         ElementWrapper: function(el) {
16768                 /**
16769                  * The element
16770                  * @property el
16771                  */
16772                 this.el = el || null;
16773                 /**
16774                  * The element id
16775                  * @property id
16776                  */
16777                 this.id = this.el && el.id;
16778                 /**
16779                  * A reference to the style property
16780                  * @property css
16781                  */
16782                 this.css = this.el && el.style;
16783             },
16784
16785         /**
16786          * Returns the X position of an html element
16787          * @method getPosX
16788          * @param el the element for which to get the position
16789          * @return {int} the X coordinate
16790          * @for DragDropMgr
16791          * @deprecated use Roo.lib.Dom.getX instead
16792          * @static
16793          */
16794         getPosX: function(el) {
16795             return Roo.lib.Dom.getX(el);
16796         },
16797
16798         /**
16799          * Returns the Y position of an html element
16800          * @method getPosY
16801          * @param el the element for which to get the position
16802          * @return {int} the Y coordinate
16803          * @deprecated use Roo.lib.Dom.getY instead
16804          * @static
16805          */
16806         getPosY: function(el) {
16807             return Roo.lib.Dom.getY(el);
16808         },
16809
16810         /**
16811          * Swap two nodes.  In IE, we use the native method, for others we
16812          * emulate the IE behavior
16813          * @method swapNode
16814          * @param n1 the first node to swap
16815          * @param n2 the other node to swap
16816          * @static
16817          */
16818         swapNode: function(n1, n2) {
16819             if (n1.swapNode) {
16820                 n1.swapNode(n2);
16821             } else {
16822                 var p = n2.parentNode;
16823                 var s = n2.nextSibling;
16824
16825                 if (s == n1) {
16826                     p.insertBefore(n1, n2);
16827                 } else if (n2 == n1.nextSibling) {
16828                     p.insertBefore(n2, n1);
16829                 } else {
16830                     n1.parentNode.replaceChild(n2, n1);
16831                     p.insertBefore(n1, s);
16832                 }
16833             }
16834         },
16835
16836         /**
16837          * Returns the current scroll position
16838          * @method getScroll
16839          * @private
16840          * @static
16841          */
16842         getScroll: function () {
16843             var t, l, dde=document.documentElement, db=document.body;
16844             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16845                 t = dde.scrollTop;
16846                 l = dde.scrollLeft;
16847             } else if (db) {
16848                 t = db.scrollTop;
16849                 l = db.scrollLeft;
16850             } else {
16851
16852             }
16853             return { top: t, left: l };
16854         },
16855
16856         /**
16857          * Returns the specified element style property
16858          * @method getStyle
16859          * @param {HTMLElement} el          the element
16860          * @param {string}      styleProp   the style property
16861          * @return {string} The value of the style property
16862          * @deprecated use Roo.lib.Dom.getStyle
16863          * @static
16864          */
16865         getStyle: function(el, styleProp) {
16866             return Roo.fly(el).getStyle(styleProp);
16867         },
16868
16869         /**
16870          * Gets the scrollTop
16871          * @method getScrollTop
16872          * @return {int} the document's scrollTop
16873          * @static
16874          */
16875         getScrollTop: function () { return this.getScroll().top; },
16876
16877         /**
16878          * Gets the scrollLeft
16879          * @method getScrollLeft
16880          * @return {int} the document's scrollTop
16881          * @static
16882          */
16883         getScrollLeft: function () { return this.getScroll().left; },
16884
16885         /**
16886          * Sets the x/y position of an element to the location of the
16887          * target element.
16888          * @method moveToEl
16889          * @param {HTMLElement} moveEl      The element to move
16890          * @param {HTMLElement} targetEl    The position reference element
16891          * @static
16892          */
16893         moveToEl: function (moveEl, targetEl) {
16894             var aCoord = Roo.lib.Dom.getXY(targetEl);
16895             Roo.lib.Dom.setXY(moveEl, aCoord);
16896         },
16897
16898         /**
16899          * Numeric array sort function
16900          * @method numericSort
16901          * @static
16902          */
16903         numericSort: function(a, b) { return (a - b); },
16904
16905         /**
16906          * Internal counter
16907          * @property _timeoutCount
16908          * @private
16909          * @static
16910          */
16911         _timeoutCount: 0,
16912
16913         /**
16914          * Trying to make the load order less important.  Without this we get
16915          * an error if this file is loaded before the Event Utility.
16916          * @method _addListeners
16917          * @private
16918          * @static
16919          */
16920         _addListeners: function() {
16921             var DDM = Roo.dd.DDM;
16922             if ( Roo.lib.Event && document ) {
16923                 DDM._onLoad();
16924             } else {
16925                 if (DDM._timeoutCount > 2000) {
16926                 } else {
16927                     setTimeout(DDM._addListeners, 10);
16928                     if (document && document.body) {
16929                         DDM._timeoutCount += 1;
16930                     }
16931                 }
16932             }
16933         },
16934
16935         /**
16936          * Recursively searches the immediate parent and all child nodes for
16937          * the handle element in order to determine wheter or not it was
16938          * clicked.
16939          * @method handleWasClicked
16940          * @param node the html element to inspect
16941          * @static
16942          */
16943         handleWasClicked: function(node, id) {
16944             if (this.isHandle(id, node.id)) {
16945                 return true;
16946             } else {
16947                 // check to see if this is a text node child of the one we want
16948                 var p = node.parentNode;
16949
16950                 while (p) {
16951                     if (this.isHandle(id, p.id)) {
16952                         return true;
16953                     } else {
16954                         p = p.parentNode;
16955                     }
16956                 }
16957             }
16958
16959             return false;
16960         }
16961
16962     };
16963
16964 }();
16965
16966 // shorter alias, save a few bytes
16967 Roo.dd.DDM = Roo.dd.DragDropMgr;
16968 Roo.dd.DDM._addListeners();
16969
16970 }/*
16971  * Based on:
16972  * Ext JS Library 1.1.1
16973  * Copyright(c) 2006-2007, Ext JS, LLC.
16974  *
16975  * Originally Released Under LGPL - original licence link has changed is not relivant.
16976  *
16977  * Fork - LGPL
16978  * <script type="text/javascript">
16979  */
16980
16981 /**
16982  * @class Roo.dd.DD
16983  * A DragDrop implementation where the linked element follows the
16984  * mouse cursor during a drag.
16985  * @extends Roo.dd.DragDrop
16986  * @constructor
16987  * @param {String} id the id of the linked element
16988  * @param {String} sGroup the group of related DragDrop items
16989  * @param {object} config an object containing configurable attributes
16990  *                Valid properties for DD:
16991  *                    scroll
16992  */
16993 Roo.dd.DD = function(id, sGroup, config) {
16994     if (id) {
16995         this.init(id, sGroup, config);
16996     }
16997 };
16998
16999 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17000
17001     /**
17002      * When set to true, the utility automatically tries to scroll the browser
17003      * window wehn a drag and drop element is dragged near the viewport boundary.
17004      * Defaults to true.
17005      * @property scroll
17006      * @type boolean
17007      */
17008     scroll: true,
17009
17010     /**
17011      * Sets the pointer offset to the distance between the linked element's top
17012      * left corner and the location the element was clicked
17013      * @method autoOffset
17014      * @param {int} iPageX the X coordinate of the click
17015      * @param {int} iPageY the Y coordinate of the click
17016      */
17017     autoOffset: function(iPageX, iPageY) {
17018         var x = iPageX - this.startPageX;
17019         var y = iPageY - this.startPageY;
17020         this.setDelta(x, y);
17021     },
17022
17023     /**
17024      * Sets the pointer offset.  You can call this directly to force the
17025      * offset to be in a particular location (e.g., pass in 0,0 to set it
17026      * to the center of the object)
17027      * @method setDelta
17028      * @param {int} iDeltaX the distance from the left
17029      * @param {int} iDeltaY the distance from the top
17030      */
17031     setDelta: function(iDeltaX, iDeltaY) {
17032         this.deltaX = iDeltaX;
17033         this.deltaY = iDeltaY;
17034     },
17035
17036     /**
17037      * Sets the drag element to the location of the mousedown or click event,
17038      * maintaining the cursor location relative to the location on the element
17039      * that was clicked.  Override this if you want to place the element in a
17040      * location other than where the cursor is.
17041      * @method setDragElPos
17042      * @param {int} iPageX the X coordinate of the mousedown or drag event
17043      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17044      */
17045     setDragElPos: function(iPageX, iPageY) {
17046         // the first time we do this, we are going to check to make sure
17047         // the element has css positioning
17048
17049         var el = this.getDragEl();
17050         this.alignElWithMouse(el, iPageX, iPageY);
17051     },
17052
17053     /**
17054      * Sets the element to the location of the mousedown or click event,
17055      * maintaining the cursor location relative to the location on the element
17056      * that was clicked.  Override this if you want to place the element in a
17057      * location other than where the cursor is.
17058      * @method alignElWithMouse
17059      * @param {HTMLElement} el the element to move
17060      * @param {int} iPageX the X coordinate of the mousedown or drag event
17061      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17062      */
17063     alignElWithMouse: function(el, iPageX, iPageY) {
17064         var oCoord = this.getTargetCoord(iPageX, iPageY);
17065         var fly = el.dom ? el : Roo.fly(el);
17066         if (!this.deltaSetXY) {
17067             var aCoord = [oCoord.x, oCoord.y];
17068             fly.setXY(aCoord);
17069             var newLeft = fly.getLeft(true);
17070             var newTop  = fly.getTop(true);
17071             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17072         } else {
17073             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17074         }
17075
17076         this.cachePosition(oCoord.x, oCoord.y);
17077         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17078         return oCoord;
17079     },
17080
17081     /**
17082      * Saves the most recent position so that we can reset the constraints and
17083      * tick marks on-demand.  We need to know this so that we can calculate the
17084      * number of pixels the element is offset from its original position.
17085      * @method cachePosition
17086      * @param iPageX the current x position (optional, this just makes it so we
17087      * don't have to look it up again)
17088      * @param iPageY the current y position (optional, this just makes it so we
17089      * don't have to look it up again)
17090      */
17091     cachePosition: function(iPageX, iPageY) {
17092         if (iPageX) {
17093             this.lastPageX = iPageX;
17094             this.lastPageY = iPageY;
17095         } else {
17096             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17097             this.lastPageX = aCoord[0];
17098             this.lastPageY = aCoord[1];
17099         }
17100     },
17101
17102     /**
17103      * Auto-scroll the window if the dragged object has been moved beyond the
17104      * visible window boundary.
17105      * @method autoScroll
17106      * @param {int} x the drag element's x position
17107      * @param {int} y the drag element's y position
17108      * @param {int} h the height of the drag element
17109      * @param {int} w the width of the drag element
17110      * @private
17111      */
17112     autoScroll: function(x, y, h, w) {
17113
17114         if (this.scroll) {
17115             // The client height
17116             var clientH = Roo.lib.Dom.getViewWidth();
17117
17118             // The client width
17119             var clientW = Roo.lib.Dom.getViewHeight();
17120
17121             // The amt scrolled down
17122             var st = this.DDM.getScrollTop();
17123
17124             // The amt scrolled right
17125             var sl = this.DDM.getScrollLeft();
17126
17127             // Location of the bottom of the element
17128             var bot = h + y;
17129
17130             // Location of the right of the element
17131             var right = w + x;
17132
17133             // The distance from the cursor to the bottom of the visible area,
17134             // adjusted so that we don't scroll if the cursor is beyond the
17135             // element drag constraints
17136             var toBot = (clientH + st - y - this.deltaY);
17137
17138             // The distance from the cursor to the right of the visible area
17139             var toRight = (clientW + sl - x - this.deltaX);
17140
17141
17142             // How close to the edge the cursor must be before we scroll
17143             // var thresh = (document.all) ? 100 : 40;
17144             var thresh = 40;
17145
17146             // How many pixels to scroll per autoscroll op.  This helps to reduce
17147             // clunky scrolling. IE is more sensitive about this ... it needs this
17148             // value to be higher.
17149             var scrAmt = (document.all) ? 80 : 30;
17150
17151             // Scroll down if we are near the bottom of the visible page and the
17152             // obj extends below the crease
17153             if ( bot > clientH && toBot < thresh ) {
17154                 window.scrollTo(sl, st + scrAmt);
17155             }
17156
17157             // Scroll up if the window is scrolled down and the top of the object
17158             // goes above the top border
17159             if ( y < st && st > 0 && y - st < thresh ) {
17160                 window.scrollTo(sl, st - scrAmt);
17161             }
17162
17163             // Scroll right if the obj is beyond the right border and the cursor is
17164             // near the border.
17165             if ( right > clientW && toRight < thresh ) {
17166                 window.scrollTo(sl + scrAmt, st);
17167             }
17168
17169             // Scroll left if the window has been scrolled to the right and the obj
17170             // extends past the left border
17171             if ( x < sl && sl > 0 && x - sl < thresh ) {
17172                 window.scrollTo(sl - scrAmt, st);
17173             }
17174         }
17175     },
17176
17177     /**
17178      * Finds the location the element should be placed if we want to move
17179      * it to where the mouse location less the click offset would place us.
17180      * @method getTargetCoord
17181      * @param {int} iPageX the X coordinate of the click
17182      * @param {int} iPageY the Y coordinate of the click
17183      * @return an object that contains the coordinates (Object.x and Object.y)
17184      * @private
17185      */
17186     getTargetCoord: function(iPageX, iPageY) {
17187
17188
17189         var x = iPageX - this.deltaX;
17190         var y = iPageY - this.deltaY;
17191
17192         if (this.constrainX) {
17193             if (x < this.minX) { x = this.minX; }
17194             if (x > this.maxX) { x = this.maxX; }
17195         }
17196
17197         if (this.constrainY) {
17198             if (y < this.minY) { y = this.minY; }
17199             if (y > this.maxY) { y = this.maxY; }
17200         }
17201
17202         x = this.getTick(x, this.xTicks);
17203         y = this.getTick(y, this.yTicks);
17204
17205
17206         return {x:x, y:y};
17207     },
17208
17209     /*
17210      * Sets up config options specific to this class. Overrides
17211      * Roo.dd.DragDrop, but all versions of this method through the
17212      * inheritance chain are called
17213      */
17214     applyConfig: function() {
17215         Roo.dd.DD.superclass.applyConfig.call(this);
17216         this.scroll = (this.config.scroll !== false);
17217     },
17218
17219     /*
17220      * Event that fires prior to the onMouseDown event.  Overrides
17221      * Roo.dd.DragDrop.
17222      */
17223     b4MouseDown: function(e) {
17224         // this.resetConstraints();
17225         this.autoOffset(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     /*
17230      * Event that fires prior to the onDrag event.  Overrides
17231      * Roo.dd.DragDrop.
17232      */
17233     b4Drag: function(e) {
17234         this.setDragElPos(e.getPageX(),
17235                             e.getPageY());
17236     },
17237
17238     toString: function() {
17239         return ("DD " + this.id);
17240     }
17241
17242     //////////////////////////////////////////////////////////////////////////
17243     // Debugging ygDragDrop events that can be overridden
17244     //////////////////////////////////////////////////////////////////////////
17245     /*
17246     startDrag: function(x, y) {
17247     },
17248
17249     onDrag: function(e) {
17250     },
17251
17252     onDragEnter: function(e, id) {
17253     },
17254
17255     onDragOver: function(e, id) {
17256     },
17257
17258     onDragOut: function(e, id) {
17259     },
17260
17261     onDragDrop: function(e, id) {
17262     },
17263
17264     endDrag: function(e) {
17265     }
17266
17267     */
17268
17269 });/*
17270  * Based on:
17271  * Ext JS Library 1.1.1
17272  * Copyright(c) 2006-2007, Ext JS, LLC.
17273  *
17274  * Originally Released Under LGPL - original licence link has changed is not relivant.
17275  *
17276  * Fork - LGPL
17277  * <script type="text/javascript">
17278  */
17279
17280 /**
17281  * @class Roo.dd.DDProxy
17282  * A DragDrop implementation that inserts an empty, bordered div into
17283  * the document that follows the cursor during drag operations.  At the time of
17284  * the click, the frame div is resized to the dimensions of the linked html
17285  * element, and moved to the exact location of the linked element.
17286  *
17287  * References to the "frame" element refer to the single proxy element that
17288  * was created to be dragged in place of all DDProxy elements on the
17289  * page.
17290  *
17291  * @extends Roo.dd.DD
17292  * @constructor
17293  * @param {String} id the id of the linked html element
17294  * @param {String} sGroup the group of related DragDrop objects
17295  * @param {object} config an object containing configurable attributes
17296  *                Valid properties for DDProxy in addition to those in DragDrop:
17297  *                   resizeFrame, centerFrame, dragElId
17298  */
17299 Roo.dd.DDProxy = function(id, sGroup, config) {
17300     if (id) {
17301         this.init(id, sGroup, config);
17302         this.initFrame();
17303     }
17304 };
17305
17306 /**
17307  * The default drag frame div id
17308  * @property Roo.dd.DDProxy.dragElId
17309  * @type String
17310  * @static
17311  */
17312 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17313
17314 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17315
17316     /**
17317      * By default we resize the drag frame to be the same size as the element
17318      * we want to drag (this is to get the frame effect).  We can turn it off
17319      * if we want a different behavior.
17320      * @property resizeFrame
17321      * @type boolean
17322      */
17323     resizeFrame: true,
17324
17325     /**
17326      * By default the frame is positioned exactly where the drag element is, so
17327      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17328      * you do not have constraints on the obj is to have the drag frame centered
17329      * around the cursor.  Set centerFrame to true for this effect.
17330      * @property centerFrame
17331      * @type boolean
17332      */
17333     centerFrame: false,
17334
17335     /**
17336      * Creates the proxy element if it does not yet exist
17337      * @method createFrame
17338      */
17339     createFrame: function() {
17340         var self = this;
17341         var body = document.body;
17342
17343         if (!body || !body.firstChild) {
17344             setTimeout( function() { self.createFrame(); }, 50 );
17345             return;
17346         }
17347
17348         var div = this.getDragEl();
17349
17350         if (!div) {
17351             div    = document.createElement("div");
17352             div.id = this.dragElId;
17353             var s  = div.style;
17354
17355             s.position   = "absolute";
17356             s.visibility = "hidden";
17357             s.cursor     = "move";
17358             s.border     = "2px solid #aaa";
17359             s.zIndex     = 999;
17360
17361             // appendChild can blow up IE if invoked prior to the window load event
17362             // while rendering a table.  It is possible there are other scenarios
17363             // that would cause this to happen as well.
17364             body.insertBefore(div, body.firstChild);
17365         }
17366     },
17367
17368     /**
17369      * Initialization for the drag frame element.  Must be called in the
17370      * constructor of all subclasses
17371      * @method initFrame
17372      */
17373     initFrame: function() {
17374         this.createFrame();
17375     },
17376
17377     applyConfig: function() {
17378         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17379
17380         this.resizeFrame = (this.config.resizeFrame !== false);
17381         this.centerFrame = (this.config.centerFrame);
17382         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17383     },
17384
17385     /**
17386      * Resizes the drag frame to the dimensions of the clicked object, positions
17387      * it over the object, and finally displays it
17388      * @method showFrame
17389      * @param {int} iPageX X click position
17390      * @param {int} iPageY Y click position
17391      * @private
17392      */
17393     showFrame: function(iPageX, iPageY) {
17394         var el = this.getEl();
17395         var dragEl = this.getDragEl();
17396         var s = dragEl.style;
17397
17398         this._resizeProxy();
17399
17400         if (this.centerFrame) {
17401             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17402                            Math.round(parseInt(s.height, 10)/2) );
17403         }
17404
17405         this.setDragElPos(iPageX, iPageY);
17406
17407         Roo.fly(dragEl).show();
17408     },
17409
17410     /**
17411      * The proxy is automatically resized to the dimensions of the linked
17412      * element when a drag is initiated, unless resizeFrame is set to false
17413      * @method _resizeProxy
17414      * @private
17415      */
17416     _resizeProxy: function() {
17417         if (this.resizeFrame) {
17418             var el = this.getEl();
17419             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17420         }
17421     },
17422
17423     // overrides Roo.dd.DragDrop
17424     b4MouseDown: function(e) {
17425         var x = e.getPageX();
17426         var y = e.getPageY();
17427         this.autoOffset(x, y);
17428         this.setDragElPos(x, y);
17429     },
17430
17431     // overrides Roo.dd.DragDrop
17432     b4StartDrag: function(x, y) {
17433         // show the drag frame
17434         this.showFrame(x, y);
17435     },
17436
17437     // overrides Roo.dd.DragDrop
17438     b4EndDrag: function(e) {
17439         Roo.fly(this.getDragEl()).hide();
17440     },
17441
17442     // overrides Roo.dd.DragDrop
17443     // By default we try to move the element to the last location of the frame.
17444     // This is so that the default behavior mirrors that of Roo.dd.DD.
17445     endDrag: function(e) {
17446
17447         var lel = this.getEl();
17448         var del = this.getDragEl();
17449
17450         // Show the drag frame briefly so we can get its position
17451         del.style.visibility = "";
17452
17453         this.beforeMove();
17454         // Hide the linked element before the move to get around a Safari
17455         // rendering bug.
17456         lel.style.visibility = "hidden";
17457         Roo.dd.DDM.moveToEl(lel, del);
17458         del.style.visibility = "hidden";
17459         lel.style.visibility = "";
17460
17461         this.afterDrag();
17462     },
17463
17464     beforeMove : function(){
17465
17466     },
17467
17468     afterDrag : function(){
17469
17470     },
17471
17472     toString: function() {
17473         return ("DDProxy " + this.id);
17474     }
17475
17476 });
17477 /*
17478  * Based on:
17479  * Ext JS Library 1.1.1
17480  * Copyright(c) 2006-2007, Ext JS, LLC.
17481  *
17482  * Originally Released Under LGPL - original licence link has changed is not relivant.
17483  *
17484  * Fork - LGPL
17485  * <script type="text/javascript">
17486  */
17487
17488  /**
17489  * @class Roo.dd.DDTarget
17490  * A DragDrop implementation that does not move, but can be a drop
17491  * target.  You would get the same result by simply omitting implementation
17492  * for the event callbacks, but this way we reduce the processing cost of the
17493  * event listener and the callbacks.
17494  * @extends Roo.dd.DragDrop
17495  * @constructor
17496  * @param {String} id the id of the element that is a drop target
17497  * @param {String} sGroup the group of related DragDrop objects
17498  * @param {object} config an object containing configurable attributes
17499  *                 Valid properties for DDTarget in addition to those in
17500  *                 DragDrop:
17501  *                    none
17502  */
17503 Roo.dd.DDTarget = function(id, sGroup, config) {
17504     if (id) {
17505         this.initTarget(id, sGroup, config);
17506     }
17507     if (config.listeners || config.events) { 
17508        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17509             listeners : config.listeners || {}, 
17510             events : config.events || {} 
17511         });    
17512     }
17513 };
17514
17515 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17516 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17517     toString: function() {
17518         return ("DDTarget " + this.id);
17519     }
17520 });
17521 /*
17522  * Based on:
17523  * Ext JS Library 1.1.1
17524  * Copyright(c) 2006-2007, Ext JS, LLC.
17525  *
17526  * Originally Released Under LGPL - original licence link has changed is not relivant.
17527  *
17528  * Fork - LGPL
17529  * <script type="text/javascript">
17530  */
17531  
17532
17533 /**
17534  * @class Roo.dd.ScrollManager
17535  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17536  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17537  * @singleton
17538  */
17539 Roo.dd.ScrollManager = function(){
17540     var ddm = Roo.dd.DragDropMgr;
17541     var els = {};
17542     var dragEl = null;
17543     var proc = {};
17544     
17545     var onStop = function(e){
17546         dragEl = null;
17547         clearProc();
17548     };
17549     
17550     var triggerRefresh = function(){
17551         if(ddm.dragCurrent){
17552              ddm.refreshCache(ddm.dragCurrent.groups);
17553         }
17554     };
17555     
17556     var doScroll = function(){
17557         if(ddm.dragCurrent){
17558             var dds = Roo.dd.ScrollManager;
17559             if(!dds.animate){
17560                 if(proc.el.scroll(proc.dir, dds.increment)){
17561                     triggerRefresh();
17562                 }
17563             }else{
17564                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17565             }
17566         }
17567     };
17568     
17569     var clearProc = function(){
17570         if(proc.id){
17571             clearInterval(proc.id);
17572         }
17573         proc.id = 0;
17574         proc.el = null;
17575         proc.dir = "";
17576     };
17577     
17578     var startProc = function(el, dir){
17579         clearProc();
17580         proc.el = el;
17581         proc.dir = dir;
17582         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17583     };
17584     
17585     var onFire = function(e, isDrop){
17586         if(isDrop || !ddm.dragCurrent){ return; }
17587         var dds = Roo.dd.ScrollManager;
17588         if(!dragEl || dragEl != ddm.dragCurrent){
17589             dragEl = ddm.dragCurrent;
17590             // refresh regions on drag start
17591             dds.refreshCache();
17592         }
17593         
17594         var xy = Roo.lib.Event.getXY(e);
17595         var pt = new Roo.lib.Point(xy[0], xy[1]);
17596         for(var id in els){
17597             var el = els[id], r = el._region;
17598             if(r && r.contains(pt) && el.isScrollable()){
17599                 if(r.bottom - pt.y <= dds.thresh){
17600                     if(proc.el != el){
17601                         startProc(el, "down");
17602                     }
17603                     return;
17604                 }else if(r.right - pt.x <= dds.thresh){
17605                     if(proc.el != el){
17606                         startProc(el, "left");
17607                     }
17608                     return;
17609                 }else if(pt.y - r.top <= dds.thresh){
17610                     if(proc.el != el){
17611                         startProc(el, "up");
17612                     }
17613                     return;
17614                 }else if(pt.x - r.left <= dds.thresh){
17615                     if(proc.el != el){
17616                         startProc(el, "right");
17617                     }
17618                     return;
17619                 }
17620             }
17621         }
17622         clearProc();
17623     };
17624     
17625     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17626     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17627     
17628     return {
17629         /**
17630          * Registers new overflow element(s) to auto scroll
17631          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17632          */
17633         register : function(el){
17634             if(el instanceof Array){
17635                 for(var i = 0, len = el.length; i < len; i++) {
17636                         this.register(el[i]);
17637                 }
17638             }else{
17639                 el = Roo.get(el);
17640                 els[el.id] = el;
17641             }
17642         },
17643         
17644         /**
17645          * Unregisters overflow element(s) so they are no longer scrolled
17646          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17647          */
17648         unregister : function(el){
17649             if(el instanceof Array){
17650                 for(var i = 0, len = el.length; i < len; i++) {
17651                         this.unregister(el[i]);
17652                 }
17653             }else{
17654                 el = Roo.get(el);
17655                 delete els[el.id];
17656             }
17657         },
17658         
17659         /**
17660          * The number of pixels from the edge of a container the pointer needs to be to 
17661          * trigger scrolling (defaults to 25)
17662          * @type Number
17663          */
17664         thresh : 25,
17665         
17666         /**
17667          * The number of pixels to scroll in each scroll increment (defaults to 50)
17668          * @type Number
17669          */
17670         increment : 100,
17671         
17672         /**
17673          * The frequency of scrolls in milliseconds (defaults to 500)
17674          * @type Number
17675          */
17676         frequency : 500,
17677         
17678         /**
17679          * True to animate the scroll (defaults to true)
17680          * @type Boolean
17681          */
17682         animate: true,
17683         
17684         /**
17685          * The animation duration in seconds - 
17686          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17687          * @type Number
17688          */
17689         animDuration: .4,
17690         
17691         /**
17692          * Manually trigger a cache refresh.
17693          */
17694         refreshCache : function(){
17695             for(var id in els){
17696                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17697                     els[id]._region = els[id].getRegion();
17698                 }
17699             }
17700         }
17701     };
17702 }();/*
17703  * Based on:
17704  * Ext JS Library 1.1.1
17705  * Copyright(c) 2006-2007, Ext JS, LLC.
17706  *
17707  * Originally Released Under LGPL - original licence link has changed is not relivant.
17708  *
17709  * Fork - LGPL
17710  * <script type="text/javascript">
17711  */
17712  
17713
17714 /**
17715  * @class Roo.dd.Registry
17716  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17717  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17718  * @singleton
17719  */
17720 Roo.dd.Registry = function(){
17721     var elements = {}; 
17722     var handles = {}; 
17723     var autoIdSeed = 0;
17724
17725     var getId = function(el, autogen){
17726         if(typeof el == "string"){
17727             return el;
17728         }
17729         var id = el.id;
17730         if(!id && autogen !== false){
17731             id = "roodd-" + (++autoIdSeed);
17732             el.id = id;
17733         }
17734         return id;
17735     };
17736     
17737     return {
17738     /**
17739      * Register a drag drop element
17740      * @param {String|HTMLElement} element The id or DOM node to register
17741      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17742      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17743      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17744      * populated in the data object (if applicable):
17745      * <pre>
17746 Value      Description<br />
17747 ---------  ------------------------------------------<br />
17748 handles    Array of DOM nodes that trigger dragging<br />
17749            for the element being registered<br />
17750 isHandle   True if the element passed in triggers<br />
17751            dragging itself, else false
17752 </pre>
17753      */
17754         register : function(el, data){
17755             data = data || {};
17756             if(typeof el == "string"){
17757                 el = document.getElementById(el);
17758             }
17759             data.ddel = el;
17760             elements[getId(el)] = data;
17761             if(data.isHandle !== false){
17762                 handles[data.ddel.id] = data;
17763             }
17764             if(data.handles){
17765                 var hs = data.handles;
17766                 for(var i = 0, len = hs.length; i < len; i++){
17767                         handles[getId(hs[i])] = data;
17768                 }
17769             }
17770         },
17771
17772     /**
17773      * Unregister a drag drop element
17774      * @param {String|HTMLElement}  element The id or DOM node to unregister
17775      */
17776         unregister : function(el){
17777             var id = getId(el, false);
17778             var data = elements[id];
17779             if(data){
17780                 delete elements[id];
17781                 if(data.handles){
17782                     var hs = data.handles;
17783                     for(var i = 0, len = hs.length; i < len; i++){
17784                         delete handles[getId(hs[i], false)];
17785                     }
17786                 }
17787             }
17788         },
17789
17790     /**
17791      * Returns the handle registered for a DOM Node by id
17792      * @param {String|HTMLElement} id The DOM node or id to look up
17793      * @return {Object} handle The custom handle data
17794      */
17795         getHandle : function(id){
17796             if(typeof id != "string"){ // must be element?
17797                 id = id.id;
17798             }
17799             return handles[id];
17800         },
17801
17802     /**
17803      * Returns the handle that is registered for the DOM node that is the target of the event
17804      * @param {Event} e The event
17805      * @return {Object} handle The custom handle data
17806      */
17807         getHandleFromEvent : function(e){
17808             var t = Roo.lib.Event.getTarget(e);
17809             return t ? handles[t.id] : null;
17810         },
17811
17812     /**
17813      * Returns a custom data object that is registered for a DOM node by id
17814      * @param {String|HTMLElement} id The DOM node or id to look up
17815      * @return {Object} data The custom data
17816      */
17817         getTarget : function(id){
17818             if(typeof id != "string"){ // must be element?
17819                 id = id.id;
17820             }
17821             return elements[id];
17822         },
17823
17824     /**
17825      * Returns a custom data object that is registered for the DOM node that is the target of the event
17826      * @param {Event} e The event
17827      * @return {Object} data The custom data
17828      */
17829         getTargetFromEvent : function(e){
17830             var t = Roo.lib.Event.getTarget(e);
17831             return t ? elements[t.id] || handles[t.id] : null;
17832         }
17833     };
17834 }();/*
17835  * Based on:
17836  * Ext JS Library 1.1.1
17837  * Copyright(c) 2006-2007, Ext JS, LLC.
17838  *
17839  * Originally Released Under LGPL - original licence link has changed is not relivant.
17840  *
17841  * Fork - LGPL
17842  * <script type="text/javascript">
17843  */
17844  
17845
17846 /**
17847  * @class Roo.dd.StatusProxy
17848  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17849  * default drag proxy used by all Roo.dd components.
17850  * @constructor
17851  * @param {Object} config
17852  */
17853 Roo.dd.StatusProxy = function(config){
17854     Roo.apply(this, config);
17855     this.id = this.id || Roo.id();
17856     this.el = new Roo.Layer({
17857         dh: {
17858             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17859                 {tag: "div", cls: "x-dd-drop-icon"},
17860                 {tag: "div", cls: "x-dd-drag-ghost"}
17861             ]
17862         }, 
17863         shadow: !config || config.shadow !== false
17864     });
17865     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17866     this.dropStatus = this.dropNotAllowed;
17867 };
17868
17869 Roo.dd.StatusProxy.prototype = {
17870     /**
17871      * @cfg {String} dropAllowed
17872      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17873      */
17874     dropAllowed : "x-dd-drop-ok",
17875     /**
17876      * @cfg {String} dropNotAllowed
17877      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17878      */
17879     dropNotAllowed : "x-dd-drop-nodrop",
17880
17881     /**
17882      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17883      * over the current target element.
17884      * @param {String} cssClass The css class for the new drop status indicator image
17885      */
17886     setStatus : function(cssClass){
17887         cssClass = cssClass || this.dropNotAllowed;
17888         if(this.dropStatus != cssClass){
17889             this.el.replaceClass(this.dropStatus, cssClass);
17890             this.dropStatus = cssClass;
17891         }
17892     },
17893
17894     /**
17895      * Resets the status indicator to the default dropNotAllowed value
17896      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17897      */
17898     reset : function(clearGhost){
17899         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17900         this.dropStatus = this.dropNotAllowed;
17901         if(clearGhost){
17902             this.ghost.update("");
17903         }
17904     },
17905
17906     /**
17907      * Updates the contents of the ghost element
17908      * @param {String} html The html that will replace the current innerHTML of the ghost element
17909      */
17910     update : function(html){
17911         if(typeof html == "string"){
17912             this.ghost.update(html);
17913         }else{
17914             this.ghost.update("");
17915             html.style.margin = "0";
17916             this.ghost.dom.appendChild(html);
17917         }
17918         // ensure float = none set?? cant remember why though.
17919         var el = this.ghost.dom.firstChild;
17920                 if(el){
17921                         Roo.fly(el).setStyle('float', 'none');
17922                 }
17923     },
17924     
17925     /**
17926      * Returns the underlying proxy {@link Roo.Layer}
17927      * @return {Roo.Layer} el
17928     */
17929     getEl : function(){
17930         return this.el;
17931     },
17932
17933     /**
17934      * Returns the ghost element
17935      * @return {Roo.Element} el
17936      */
17937     getGhost : function(){
17938         return this.ghost;
17939     },
17940
17941     /**
17942      * Hides the proxy
17943      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17944      */
17945     hide : function(clear){
17946         this.el.hide();
17947         if(clear){
17948             this.reset(true);
17949         }
17950     },
17951
17952     /**
17953      * Stops the repair animation if it's currently running
17954      */
17955     stop : function(){
17956         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17957             this.anim.stop();
17958         }
17959     },
17960
17961     /**
17962      * Displays this proxy
17963      */
17964     show : function(){
17965         this.el.show();
17966     },
17967
17968     /**
17969      * Force the Layer to sync its shadow and shim positions to the element
17970      */
17971     sync : function(){
17972         this.el.sync();
17973     },
17974
17975     /**
17976      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17977      * invalid drop operation by the item being dragged.
17978      * @param {Array} xy The XY position of the element ([x, y])
17979      * @param {Function} callback The function to call after the repair is complete
17980      * @param {Object} scope The scope in which to execute the callback
17981      */
17982     repair : function(xy, callback, scope){
17983         this.callback = callback;
17984         this.scope = scope;
17985         if(xy && this.animRepair !== false){
17986             this.el.addClass("x-dd-drag-repair");
17987             this.el.hideUnders(true);
17988             this.anim = this.el.shift({
17989                 duration: this.repairDuration || .5,
17990                 easing: 'easeOut',
17991                 xy: xy,
17992                 stopFx: true,
17993                 callback: this.afterRepair,
17994                 scope: this
17995             });
17996         }else{
17997             this.afterRepair();
17998         }
17999     },
18000
18001     // private
18002     afterRepair : function(){
18003         this.hide(true);
18004         if(typeof this.callback == "function"){
18005             this.callback.call(this.scope || this);
18006         }
18007         this.callback = null;
18008         this.scope = null;
18009     }
18010 };/*
18011  * Based on:
18012  * Ext JS Library 1.1.1
18013  * Copyright(c) 2006-2007, Ext JS, LLC.
18014  *
18015  * Originally Released Under LGPL - original licence link has changed is not relivant.
18016  *
18017  * Fork - LGPL
18018  * <script type="text/javascript">
18019  */
18020
18021 /**
18022  * @class Roo.dd.DragSource
18023  * @extends Roo.dd.DDProxy
18024  * A simple class that provides the basic implementation needed to make any element draggable.
18025  * @constructor
18026  * @param {String/HTMLElement/Element} el The container element
18027  * @param {Object} config
18028  */
18029 Roo.dd.DragSource = function(el, config){
18030     this.el = Roo.get(el);
18031     this.dragData = {};
18032     
18033     Roo.apply(this, config);
18034     
18035     if(!this.proxy){
18036         this.proxy = new Roo.dd.StatusProxy();
18037     }
18038
18039     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18040           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18041     
18042     this.dragging = false;
18043 };
18044
18045 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18046     /**
18047      * @cfg {String} dropAllowed
18048      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18049      */
18050     dropAllowed : "x-dd-drop-ok",
18051     /**
18052      * @cfg {String} dropNotAllowed
18053      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18054      */
18055     dropNotAllowed : "x-dd-drop-nodrop",
18056
18057     /**
18058      * Returns the data object associated with this drag source
18059      * @return {Object} data An object containing arbitrary data
18060      */
18061     getDragData : function(e){
18062         return this.dragData;
18063     },
18064
18065     // private
18066     onDragEnter : function(e, id){
18067         var target = Roo.dd.DragDropMgr.getDDById(id);
18068         this.cachedTarget = target;
18069         if(this.beforeDragEnter(target, e, id) !== false){
18070             if(target.isNotifyTarget){
18071                 var status = target.notifyEnter(this, e, this.dragData);
18072                 this.proxy.setStatus(status);
18073             }else{
18074                 this.proxy.setStatus(this.dropAllowed);
18075             }
18076             
18077             if(this.afterDragEnter){
18078                 /**
18079                  * An empty function by default, but provided so that you can perform a custom action
18080                  * when the dragged item enters the drop target by providing an implementation.
18081                  * @param {Roo.dd.DragDrop} target The drop target
18082                  * @param {Event} e The event object
18083                  * @param {String} id The id of the dragged element
18084                  * @method afterDragEnter
18085                  */
18086                 this.afterDragEnter(target, e, id);
18087             }
18088         }
18089     },
18090
18091     /**
18092      * An empty function by default, but provided so that you can perform a custom action
18093      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18094      * @param {Roo.dd.DragDrop} target The drop target
18095      * @param {Event} e The event object
18096      * @param {String} id The id of the dragged element
18097      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18098      */
18099     beforeDragEnter : function(target, e, id){
18100         return true;
18101     },
18102
18103     // private
18104     alignElWithMouse: function() {
18105         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18106         this.proxy.sync();
18107     },
18108
18109     // private
18110     onDragOver : function(e, id){
18111         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18112         if(this.beforeDragOver(target, e, id) !== false){
18113             if(target.isNotifyTarget){
18114                 var status = target.notifyOver(this, e, this.dragData);
18115                 this.proxy.setStatus(status);
18116             }
18117
18118             if(this.afterDragOver){
18119                 /**
18120                  * An empty function by default, but provided so that you can perform a custom action
18121                  * while the dragged item is over the drop target by providing an implementation.
18122                  * @param {Roo.dd.DragDrop} target The drop target
18123                  * @param {Event} e The event object
18124                  * @param {String} id The id of the dragged element
18125                  * @method afterDragOver
18126                  */
18127                 this.afterDragOver(target, e, id);
18128             }
18129         }
18130     },
18131
18132     /**
18133      * An empty function by default, but provided so that you can perform a custom action
18134      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18135      * @param {Roo.dd.DragDrop} target The drop target
18136      * @param {Event} e The event object
18137      * @param {String} id The id of the dragged element
18138      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18139      */
18140     beforeDragOver : function(target, e, id){
18141         return true;
18142     },
18143
18144     // private
18145     onDragOut : function(e, id){
18146         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18147         if(this.beforeDragOut(target, e, id) !== false){
18148             if(target.isNotifyTarget){
18149                 target.notifyOut(this, e, this.dragData);
18150             }
18151             this.proxy.reset();
18152             if(this.afterDragOut){
18153                 /**
18154                  * An empty function by default, but provided so that you can perform a custom action
18155                  * after the dragged item is dragged out of the target without dropping.
18156                  * @param {Roo.dd.DragDrop} target The drop target
18157                  * @param {Event} e The event object
18158                  * @param {String} id The id of the dragged element
18159                  * @method afterDragOut
18160                  */
18161                 this.afterDragOut(target, e, id);
18162             }
18163         }
18164         this.cachedTarget = null;
18165     },
18166
18167     /**
18168      * An empty function by default, but provided so that you can perform a custom action before the dragged
18169      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18170      * @param {Roo.dd.DragDrop} target The drop target
18171      * @param {Event} e The event object
18172      * @param {String} id The id of the dragged element
18173      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18174      */
18175     beforeDragOut : function(target, e, id){
18176         return true;
18177     },
18178     
18179     // private
18180     onDragDrop : function(e, id){
18181         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18182         if(this.beforeDragDrop(target, e, id) !== false){
18183             if(target.isNotifyTarget){
18184                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18185                     this.onValidDrop(target, e, id);
18186                 }else{
18187                     this.onInvalidDrop(target, e, id);
18188                 }
18189             }else{
18190                 this.onValidDrop(target, e, id);
18191             }
18192             
18193             if(this.afterDragDrop){
18194                 /**
18195                  * An empty function by default, but provided so that you can perform a custom action
18196                  * after a valid drag drop has occurred by providing an implementation.
18197                  * @param {Roo.dd.DragDrop} target The drop target
18198                  * @param {Event} e The event object
18199                  * @param {String} id The id of the dropped element
18200                  * @method afterDragDrop
18201                  */
18202                 this.afterDragDrop(target, e, id);
18203             }
18204         }
18205         delete this.cachedTarget;
18206     },
18207
18208     /**
18209      * An empty function by default, but provided so that you can perform a custom action before the dragged
18210      * item is dropped onto the target and optionally cancel the onDragDrop.
18211      * @param {Roo.dd.DragDrop} target The drop target
18212      * @param {Event} e The event object
18213      * @param {String} id The id of the dragged element
18214      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18215      */
18216     beforeDragDrop : function(target, e, id){
18217         return true;
18218     },
18219
18220     // private
18221     onValidDrop : function(target, e, id){
18222         this.hideProxy();
18223         if(this.afterValidDrop){
18224             /**
18225              * An empty function by default, but provided so that you can perform a custom action
18226              * after a valid drop has occurred by providing an implementation.
18227              * @param {Object} target The target DD 
18228              * @param {Event} e The event object
18229              * @param {String} id The id of the dropped element
18230              * @method afterInvalidDrop
18231              */
18232             this.afterValidDrop(target, e, id);
18233         }
18234     },
18235
18236     // private
18237     getRepairXY : function(e, data){
18238         return this.el.getXY();  
18239     },
18240
18241     // private
18242     onInvalidDrop : function(target, e, id){
18243         this.beforeInvalidDrop(target, e, id);
18244         if(this.cachedTarget){
18245             if(this.cachedTarget.isNotifyTarget){
18246                 this.cachedTarget.notifyOut(this, e, this.dragData);
18247             }
18248             this.cacheTarget = null;
18249         }
18250         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18251
18252         if(this.afterInvalidDrop){
18253             /**
18254              * An empty function by default, but provided so that you can perform a custom action
18255              * after an invalid drop has occurred by providing an implementation.
18256              * @param {Event} e The event object
18257              * @param {String} id The id of the dropped element
18258              * @method afterInvalidDrop
18259              */
18260             this.afterInvalidDrop(e, id);
18261         }
18262     },
18263
18264     // private
18265     afterRepair : function(){
18266         if(Roo.enableFx){
18267             this.el.highlight(this.hlColor || "c3daf9");
18268         }
18269         this.dragging = false;
18270     },
18271
18272     /**
18273      * An empty function by default, but provided so that you can perform a custom action after an invalid
18274      * drop has occurred.
18275      * @param {Roo.dd.DragDrop} target The drop target
18276      * @param {Event} e The event object
18277      * @param {String} id The id of the dragged element
18278      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18279      */
18280     beforeInvalidDrop : function(target, e, id){
18281         return true;
18282     },
18283
18284     // private
18285     handleMouseDown : function(e){
18286         if(this.dragging) {
18287             return;
18288         }
18289         var data = this.getDragData(e);
18290         if(data && this.onBeforeDrag(data, e) !== false){
18291             this.dragData = data;
18292             this.proxy.stop();
18293             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18294         } 
18295     },
18296
18297     /**
18298      * An empty function by default, but provided so that you can perform a custom action before the initial
18299      * drag event begins and optionally cancel it.
18300      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18301      * @param {Event} e The event object
18302      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18303      */
18304     onBeforeDrag : function(data, e){
18305         return true;
18306     },
18307
18308     /**
18309      * An empty function by default, but provided so that you can perform a custom action once the initial
18310      * drag event has begun.  The drag cannot be canceled from this function.
18311      * @param {Number} x The x position of the click on the dragged object
18312      * @param {Number} y The y position of the click on the dragged object
18313      */
18314     onStartDrag : Roo.emptyFn,
18315
18316     // private - YUI override
18317     startDrag : function(x, y){
18318         this.proxy.reset();
18319         this.dragging = true;
18320         this.proxy.update("");
18321         this.onInitDrag(x, y);
18322         this.proxy.show();
18323     },
18324
18325     // private
18326     onInitDrag : function(x, y){
18327         var clone = this.el.dom.cloneNode(true);
18328         clone.id = Roo.id(); // prevent duplicate ids
18329         this.proxy.update(clone);
18330         this.onStartDrag(x, y);
18331         return true;
18332     },
18333
18334     /**
18335      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18336      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18337      */
18338     getProxy : function(){
18339         return this.proxy;  
18340     },
18341
18342     /**
18343      * Hides the drag source's {@link Roo.dd.StatusProxy}
18344      */
18345     hideProxy : function(){
18346         this.proxy.hide();  
18347         this.proxy.reset(true);
18348         this.dragging = false;
18349     },
18350
18351     // private
18352     triggerCacheRefresh : function(){
18353         Roo.dd.DDM.refreshCache(this.groups);
18354     },
18355
18356     // private - override to prevent hiding
18357     b4EndDrag: function(e) {
18358     },
18359
18360     // private - override to prevent moving
18361     endDrag : function(e){
18362         this.onEndDrag(this.dragData, e);
18363     },
18364
18365     // private
18366     onEndDrag : function(data, e){
18367     },
18368     
18369     // private - pin to cursor
18370     autoOffset : function(x, y) {
18371         this.setDelta(-12, -20);
18372     }    
18373 });/*
18374  * Based on:
18375  * Ext JS Library 1.1.1
18376  * Copyright(c) 2006-2007, Ext JS, LLC.
18377  *
18378  * Originally Released Under LGPL - original licence link has changed is not relivant.
18379  *
18380  * Fork - LGPL
18381  * <script type="text/javascript">
18382  */
18383
18384
18385 /**
18386  * @class Roo.dd.DropTarget
18387  * @extends Roo.dd.DDTarget
18388  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18389  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18390  * @constructor
18391  * @param {String/HTMLElement/Element} el The container element
18392  * @param {Object} config
18393  */
18394 Roo.dd.DropTarget = function(el, config){
18395     this.el = Roo.get(el);
18396     
18397     var listeners = false; ;
18398     if (config && config.listeners) {
18399         listeners= config.listeners;
18400         delete config.listeners;
18401     }
18402     Roo.apply(this, config);
18403     
18404     if(this.containerScroll){
18405         Roo.dd.ScrollManager.register(this.el);
18406     }
18407     this.addEvents( {
18408          /**
18409          * @scope Roo.dd.DropTarget
18410          */
18411          
18412          /**
18413          * @event enter
18414          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18415          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18416          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18417          * 
18418          * IMPORTANT : it should set this.overClass and this.dropAllowed
18419          * 
18420          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18421          * @param {Event} e The event
18422          * @param {Object} data An object containing arbitrary data supplied by the drag source
18423          */
18424         "enter" : true,
18425         
18426          /**
18427          * @event over
18428          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18429          * This method will be called on every mouse movement while the drag source is over the drop target.
18430          * This default implementation simply returns the dropAllowed config value.
18431          * 
18432          * IMPORTANT : it should set this.dropAllowed
18433          * 
18434          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18435          * @param {Event} e The event
18436          * @param {Object} data An object containing arbitrary data supplied by the drag source
18437          
18438          */
18439         "over" : true,
18440         /**
18441          * @event out
18442          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18443          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18444          * overClass (if any) from the drop element.
18445          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18446          * @param {Event} e The event
18447          * @param {Object} data An object containing arbitrary data supplied by the drag source
18448          */
18449          "out" : true,
18450          
18451         /**
18452          * @event drop
18453          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18454          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18455          * implementation that does something to process the drop event and returns true so that the drag source's
18456          * repair action does not run.
18457          * 
18458          * IMPORTANT : it should set this.success
18459          * 
18460          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18461          * @param {Event} e The event
18462          * @param {Object} data An object containing arbitrary data supplied by the drag source
18463         */
18464          "drop" : true
18465     });
18466             
18467      
18468     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18469         this.el.dom, 
18470         this.ddGroup || this.group,
18471         {
18472             isTarget: true,
18473             listeners : listeners || {} 
18474            
18475         
18476         }
18477     );
18478
18479 };
18480
18481 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18482     /**
18483      * @cfg {String} overClass
18484      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18485      */
18486      /**
18487      * @cfg {String} ddGroup
18488      * The drag drop group to handle drop events for
18489      */
18490      
18491     /**
18492      * @cfg {String} dropAllowed
18493      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18494      */
18495     dropAllowed : "x-dd-drop-ok",
18496     /**
18497      * @cfg {String} dropNotAllowed
18498      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18499      */
18500     dropNotAllowed : "x-dd-drop-nodrop",
18501     /**
18502      * @cfg {boolean} success
18503      * set this after drop listener.. 
18504      */
18505     success : false,
18506     /**
18507      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18508      * if the drop point is valid for over/enter..
18509      */
18510     valid : false,
18511     // private
18512     isTarget : true,
18513
18514     // private
18515     isNotifyTarget : true,
18516     
18517     /**
18518      * @hide
18519      */
18520     notifyEnter : function(dd, e, data)
18521     {
18522         this.valid = true;
18523         this.fireEvent('enter', dd, e, data);
18524         if(this.overClass){
18525             this.el.addClass(this.overClass);
18526         }
18527         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18528             this.valid ? this.dropAllowed : this.dropNotAllowed
18529         );
18530     },
18531
18532     /**
18533      * @hide
18534      */
18535     notifyOver : function(dd, e, data)
18536     {
18537         this.valid = true;
18538         this.fireEvent('over', dd, e, data);
18539         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18540             this.valid ? this.dropAllowed : this.dropNotAllowed
18541         );
18542     },
18543
18544     /**
18545      * @hide
18546      */
18547     notifyOut : function(dd, e, data)
18548     {
18549         this.fireEvent('out', dd, e, data);
18550         if(this.overClass){
18551             this.el.removeClass(this.overClass);
18552         }
18553     },
18554
18555     /**
18556      * @hide
18557      */
18558     notifyDrop : function(dd, e, data)
18559     {
18560         this.success = false;
18561         this.fireEvent('drop', dd, e, data);
18562         return this.success;
18563     }
18564 });/*
18565  * Based on:
18566  * Ext JS Library 1.1.1
18567  * Copyright(c) 2006-2007, Ext JS, LLC.
18568  *
18569  * Originally Released Under LGPL - original licence link has changed is not relivant.
18570  *
18571  * Fork - LGPL
18572  * <script type="text/javascript">
18573  */
18574
18575
18576 /**
18577  * @class Roo.dd.DragZone
18578  * @extends Roo.dd.DragSource
18579  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18580  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18581  * @constructor
18582  * @param {String/HTMLElement/Element} el The container element
18583  * @param {Object} config
18584  */
18585 Roo.dd.DragZone = function(el, config){
18586     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18587     if(this.containerScroll){
18588         Roo.dd.ScrollManager.register(this.el);
18589     }
18590 };
18591
18592 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18593     /**
18594      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18595      * for auto scrolling during drag operations.
18596      */
18597     /**
18598      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18599      * method after a failed drop (defaults to "c3daf9" - light blue)
18600      */
18601
18602     /**
18603      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18604      * for a valid target to drag based on the mouse down. Override this method
18605      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18606      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18607      * @param {EventObject} e The mouse down event
18608      * @return {Object} The dragData
18609      */
18610     getDragData : function(e){
18611         return Roo.dd.Registry.getHandleFromEvent(e);
18612     },
18613     
18614     /**
18615      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18616      * this.dragData.ddel
18617      * @param {Number} x The x position of the click on the dragged object
18618      * @param {Number} y The y position of the click on the dragged object
18619      * @return {Boolean} true to continue the drag, false to cancel
18620      */
18621     onInitDrag : function(x, y){
18622         this.proxy.update(this.dragData.ddel.cloneNode(true));
18623         this.onStartDrag(x, y);
18624         return true;
18625     },
18626     
18627     /**
18628      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18629      */
18630     afterRepair : function(){
18631         if(Roo.enableFx){
18632             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18633         }
18634         this.dragging = false;
18635     },
18636
18637     /**
18638      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18639      * the XY of this.dragData.ddel
18640      * @param {EventObject} e The mouse up event
18641      * @return {Array} The xy location (e.g. [100, 200])
18642      */
18643     getRepairXY : function(e){
18644         return Roo.Element.fly(this.dragData.ddel).getXY();  
18645     }
18646 });/*
18647  * Based on:
18648  * Ext JS Library 1.1.1
18649  * Copyright(c) 2006-2007, Ext JS, LLC.
18650  *
18651  * Originally Released Under LGPL - original licence link has changed is not relivant.
18652  *
18653  * Fork - LGPL
18654  * <script type="text/javascript">
18655  */
18656 /**
18657  * @class Roo.dd.DropZone
18658  * @extends Roo.dd.DropTarget
18659  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18660  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18661  * @constructor
18662  * @param {String/HTMLElement/Element} el The container element
18663  * @param {Object} config
18664  */
18665 Roo.dd.DropZone = function(el, config){
18666     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18667 };
18668
18669 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18670     /**
18671      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18672      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18673      * provide your own custom lookup.
18674      * @param {Event} e The event
18675      * @return {Object} data The custom data
18676      */
18677     getTargetFromEvent : function(e){
18678         return Roo.dd.Registry.getTargetFromEvent(e);
18679     },
18680
18681     /**
18682      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18683      * that it has registered.  This method has no default implementation and should be overridden to provide
18684      * node-specific processing if necessary.
18685      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18686      * {@link #getTargetFromEvent} for this node)
18687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18688      * @param {Event} e The event
18689      * @param {Object} data An object containing arbitrary data supplied by the drag source
18690      */
18691     onNodeEnter : function(n, dd, e, data){
18692         
18693     },
18694
18695     /**
18696      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18697      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18698      * overridden to provide the proper feedback.
18699      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18700      * {@link #getTargetFromEvent} for this node)
18701      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18702      * @param {Event} e The event
18703      * @param {Object} data An object containing arbitrary data supplied by the drag source
18704      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18705      * underlying {@link Roo.dd.StatusProxy} can be updated
18706      */
18707     onNodeOver : function(n, dd, e, data){
18708         return this.dropAllowed;
18709     },
18710
18711     /**
18712      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18713      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18714      * node-specific processing if necessary.
18715      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18716      * {@link #getTargetFromEvent} for this node)
18717      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18718      * @param {Event} e The event
18719      * @param {Object} data An object containing arbitrary data supplied by the drag source
18720      */
18721     onNodeOut : function(n, dd, e, data){
18722         
18723     },
18724
18725     /**
18726      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18727      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18728      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18729      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18730      * {@link #getTargetFromEvent} for this node)
18731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18732      * @param {Event} e The event
18733      * @param {Object} data An object containing arbitrary data supplied by the drag source
18734      * @return {Boolean} True if the drop was valid, else false
18735      */
18736     onNodeDrop : function(n, dd, e, data){
18737         return false;
18738     },
18739
18740     /**
18741      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18742      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18743      * it should be overridden to provide the proper feedback if necessary.
18744      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18745      * @param {Event} e The event
18746      * @param {Object} data An object containing arbitrary data supplied by the drag source
18747      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18748      * underlying {@link Roo.dd.StatusProxy} can be updated
18749      */
18750     onContainerOver : function(dd, e, data){
18751         return this.dropNotAllowed;
18752     },
18753
18754     /**
18755      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18756      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18757      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18758      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18759      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18760      * @param {Event} e The event
18761      * @param {Object} data An object containing arbitrary data supplied by the drag source
18762      * @return {Boolean} True if the drop was valid, else false
18763      */
18764     onContainerDrop : function(dd, e, data){
18765         return false;
18766     },
18767
18768     /**
18769      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18770      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18771      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18772      * you should override this method and provide a custom implementation.
18773      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18774      * @param {Event} e The event
18775      * @param {Object} data An object containing arbitrary data supplied by the drag source
18776      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18777      * underlying {@link Roo.dd.StatusProxy} can be updated
18778      */
18779     notifyEnter : function(dd, e, data){
18780         return this.dropNotAllowed;
18781     },
18782
18783     /**
18784      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18785      * This method will be called on every mouse movement while the drag source is over the drop zone.
18786      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18787      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18788      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18789      * registered node, it will call {@link #onContainerOver}.
18790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18791      * @param {Event} e The event
18792      * @param {Object} data An object containing arbitrary data supplied by the drag source
18793      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18794      * underlying {@link Roo.dd.StatusProxy} can be updated
18795      */
18796     notifyOver : function(dd, e, data){
18797         var n = this.getTargetFromEvent(e);
18798         if(!n){ // not over valid drop target
18799             if(this.lastOverNode){
18800                 this.onNodeOut(this.lastOverNode, dd, e, data);
18801                 this.lastOverNode = null;
18802             }
18803             return this.onContainerOver(dd, e, data);
18804         }
18805         if(this.lastOverNode != n){
18806             if(this.lastOverNode){
18807                 this.onNodeOut(this.lastOverNode, dd, e, data);
18808             }
18809             this.onNodeEnter(n, dd, e, data);
18810             this.lastOverNode = n;
18811         }
18812         return this.onNodeOver(n, dd, e, data);
18813     },
18814
18815     /**
18816      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18817      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18818      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18819      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18820      * @param {Event} e The event
18821      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18822      */
18823     notifyOut : function(dd, e, data){
18824         if(this.lastOverNode){
18825             this.onNodeOut(this.lastOverNode, dd, e, data);
18826             this.lastOverNode = null;
18827         }
18828     },
18829
18830     /**
18831      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18832      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18833      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18834      * otherwise it will call {@link #onContainerDrop}.
18835      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18836      * @param {Event} e The event
18837      * @param {Object} data An object containing arbitrary data supplied by the drag source
18838      * @return {Boolean} True if the drop was valid, else false
18839      */
18840     notifyDrop : function(dd, e, data){
18841         if(this.lastOverNode){
18842             this.onNodeOut(this.lastOverNode, dd, e, data);
18843             this.lastOverNode = null;
18844         }
18845         var n = this.getTargetFromEvent(e);
18846         return n ?
18847             this.onNodeDrop(n, dd, e, data) :
18848             this.onContainerDrop(dd, e, data);
18849     },
18850
18851     // private
18852     triggerCacheRefresh : function(){
18853         Roo.dd.DDM.refreshCache(this.groups);
18854     }  
18855 });/*
18856  * Based on:
18857  * Ext JS Library 1.1.1
18858  * Copyright(c) 2006-2007, Ext JS, LLC.
18859  *
18860  * Originally Released Under LGPL - original licence link has changed is not relivant.
18861  *
18862  * Fork - LGPL
18863  * <script type="text/javascript">
18864  */
18865
18866
18867 /**
18868  * @class Roo.data.SortTypes
18869  * @singleton
18870  * Defines the default sorting (casting?) comparison functions used when sorting data.
18871  */
18872 Roo.data.SortTypes = {
18873     /**
18874      * Default sort that does nothing
18875      * @param {Mixed} s The value being converted
18876      * @return {Mixed} The comparison value
18877      */
18878     none : function(s){
18879         return s;
18880     },
18881     
18882     /**
18883      * The regular expression used to strip tags
18884      * @type {RegExp}
18885      * @property
18886      */
18887     stripTagsRE : /<\/?[^>]+>/gi,
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asText : function(s){
18895         return String(s).replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Strips all HTML tags to sort on text only - Case insensitive
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCText : function(s){
18904         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18905     },
18906     
18907     /**
18908      * Case insensitive string
18909      * @param {Mixed} s The value being converted
18910      * @return {String} The comparison value
18911      */
18912     asUCString : function(s) {
18913         return String(s).toUpperCase();
18914     },
18915     
18916     /**
18917      * Date sorting
18918      * @param {Mixed} s The value being converted
18919      * @return {Number} The comparison value
18920      */
18921     asDate : function(s) {
18922         if(!s){
18923             return 0;
18924         }
18925         if(s instanceof Date){
18926             return s.getTime();
18927         }
18928         return Date.parse(String(s));
18929     },
18930     
18931     /**
18932      * Float sorting
18933      * @param {Mixed} s The value being converted
18934      * @return {Float} The comparison value
18935      */
18936     asFloat : function(s) {
18937         var val = parseFloat(String(s).replace(/,/g, ""));
18938         if(isNaN(val)) val = 0;
18939         return val;
18940     },
18941     
18942     /**
18943      * Integer sorting
18944      * @param {Mixed} s The value being converted
18945      * @return {Number} The comparison value
18946      */
18947     asInt : function(s) {
18948         var val = parseInt(String(s).replace(/,/g, ""));
18949         if(isNaN(val)) val = 0;
18950         return val;
18951     }
18952 };/*
18953  * Based on:
18954  * Ext JS Library 1.1.1
18955  * Copyright(c) 2006-2007, Ext JS, LLC.
18956  *
18957  * Originally Released Under LGPL - original licence link has changed is not relivant.
18958  *
18959  * Fork - LGPL
18960  * <script type="text/javascript">
18961  */
18962
18963 /**
18964 * @class Roo.data.Record
18965  * Instances of this class encapsulate both record <em>definition</em> information, and record
18966  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18967  * to access Records cached in an {@link Roo.data.Store} object.<br>
18968  * <p>
18969  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18970  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18971  * objects.<br>
18972  * <p>
18973  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18974  * @constructor
18975  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18976  * {@link #create}. The parameters are the same.
18977  * @param {Array} data An associative Array of data values keyed by the field name.
18978  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18979  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18980  * not specified an integer id is generated.
18981  */
18982 Roo.data.Record = function(data, id){
18983     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18984     this.data = data;
18985 };
18986
18987 /**
18988  * Generate a constructor for a specific record layout.
18989  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18990  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18991  * Each field definition object may contain the following properties: <ul>
18992  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18993  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18994  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18995  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18996  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18997  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18998  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18999  * this may be omitted.</p></li>
19000  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19001  * <ul><li>auto (Default, implies no conversion)</li>
19002  * <li>string</li>
19003  * <li>int</li>
19004  * <li>float</li>
19005  * <li>boolean</li>
19006  * <li>date</li></ul></p></li>
19007  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19008  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19009  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19010  * by the Reader into an object that will be stored in the Record. It is passed the
19011  * following parameters:<ul>
19012  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19013  * </ul></p></li>
19014  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19015  * </ul>
19016  * <br>usage:<br><pre><code>
19017 var TopicRecord = Roo.data.Record.create(
19018     {name: 'title', mapping: 'topic_title'},
19019     {name: 'author', mapping: 'username'},
19020     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19021     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19022     {name: 'lastPoster', mapping: 'user2'},
19023     {name: 'excerpt', mapping: 'post_text'}
19024 );
19025
19026 var myNewRecord = new TopicRecord({
19027     title: 'Do my job please',
19028     author: 'noobie',
19029     totalPosts: 1,
19030     lastPost: new Date(),
19031     lastPoster: 'Animal',
19032     excerpt: 'No way dude!'
19033 });
19034 myStore.add(myNewRecord);
19035 </code></pre>
19036  * @method create
19037  * @static
19038  */
19039 Roo.data.Record.create = function(o){
19040     var f = function(){
19041         f.superclass.constructor.apply(this, arguments);
19042     };
19043     Roo.extend(f, Roo.data.Record);
19044     var p = f.prototype;
19045     p.fields = new Roo.util.MixedCollection(false, function(field){
19046         return field.name;
19047     });
19048     for(var i = 0, len = o.length; i < len; i++){
19049         p.fields.add(new Roo.data.Field(o[i]));
19050     }
19051     f.getField = function(name){
19052         return p.fields.get(name);  
19053     };
19054     return f;
19055 };
19056
19057 Roo.data.Record.AUTO_ID = 1000;
19058 Roo.data.Record.EDIT = 'edit';
19059 Roo.data.Record.REJECT = 'reject';
19060 Roo.data.Record.COMMIT = 'commit';
19061
19062 Roo.data.Record.prototype = {
19063     /**
19064      * Readonly flag - true if this record has been modified.
19065      * @type Boolean
19066      */
19067     dirty : false,
19068     editing : false,
19069     error: null,
19070     modified: null,
19071
19072     // private
19073     join : function(store){
19074         this.store = store;
19075     },
19076
19077     /**
19078      * Set the named field to the specified value.
19079      * @param {String} name The name of the field to set.
19080      * @param {Object} value The value to set the field to.
19081      */
19082     set : function(name, value){
19083         if(this.data[name] == value){
19084             return;
19085         }
19086         this.dirty = true;
19087         if(!this.modified){
19088             this.modified = {};
19089         }
19090         if(typeof this.modified[name] == 'undefined'){
19091             this.modified[name] = this.data[name];
19092         }
19093         this.data[name] = value;
19094         if(!this.editing && this.store){
19095             this.store.afterEdit(this);
19096         }       
19097     },
19098
19099     /**
19100      * Get the value of the named field.
19101      * @param {String} name The name of the field to get the value of.
19102      * @return {Object} The value of the field.
19103      */
19104     get : function(name){
19105         return this.data[name]; 
19106     },
19107
19108     // private
19109     beginEdit : function(){
19110         this.editing = true;
19111         this.modified = {}; 
19112     },
19113
19114     // private
19115     cancelEdit : function(){
19116         this.editing = false;
19117         delete this.modified;
19118     },
19119
19120     // private
19121     endEdit : function(){
19122         this.editing = false;
19123         if(this.dirty && this.store){
19124             this.store.afterEdit(this);
19125         }
19126     },
19127
19128     /**
19129      * Usually called by the {@link Roo.data.Store} which owns the Record.
19130      * Rejects all changes made to the Record since either creation, or the last commit operation.
19131      * Modified fields are reverted to their original values.
19132      * <p>
19133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19134      * of reject operations.
19135      */
19136     reject : function(){
19137         var m = this.modified;
19138         for(var n in m){
19139             if(typeof m[n] != "function"){
19140                 this.data[n] = m[n];
19141             }
19142         }
19143         this.dirty = false;
19144         delete this.modified;
19145         this.editing = false;
19146         if(this.store){
19147             this.store.afterReject(this);
19148         }
19149     },
19150
19151     /**
19152      * Usually called by the {@link Roo.data.Store} which owns the Record.
19153      * Commits all changes made to the Record since either creation, or the last commit operation.
19154      * <p>
19155      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19156      * of commit operations.
19157      */
19158     commit : function(){
19159         this.dirty = false;
19160         delete this.modified;
19161         this.editing = false;
19162         if(this.store){
19163             this.store.afterCommit(this);
19164         }
19165     },
19166
19167     // private
19168     hasError : function(){
19169         return this.error != null;
19170     },
19171
19172     // private
19173     clearError : function(){
19174         this.error = null;
19175     },
19176
19177     /**
19178      * Creates a copy of this record.
19179      * @param {String} id (optional) A new record id if you don't want to use this record's id
19180      * @return {Record}
19181      */
19182     copy : function(newId) {
19183         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19184     }
19185 };/*
19186  * Based on:
19187  * Ext JS Library 1.1.1
19188  * Copyright(c) 2006-2007, Ext JS, LLC.
19189  *
19190  * Originally Released Under LGPL - original licence link has changed is not relivant.
19191  *
19192  * Fork - LGPL
19193  * <script type="text/javascript">
19194  */
19195
19196
19197
19198 /**
19199  * @class Roo.data.Store
19200  * @extends Roo.util.Observable
19201  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19202  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19203  * <p>
19204  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19205  * has no knowledge of the format of the data returned by the Proxy.<br>
19206  * <p>
19207  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19208  * instances from the data object. These records are cached and made available through accessor functions.
19209  * @constructor
19210  * Creates a new Store.
19211  * @param {Object} config A config object containing the objects needed for the Store to access data,
19212  * and read the data into Records.
19213  */
19214 Roo.data.Store = function(config){
19215     this.data = new Roo.util.MixedCollection(false);
19216     this.data.getKey = function(o){
19217         return o.id;
19218     };
19219     this.baseParams = {};
19220     // private
19221     this.paramNames = {
19222         "start" : "start",
19223         "limit" : "limit",
19224         "sort" : "sort",
19225         "dir" : "dir",
19226         "multisort" : "_multisort"
19227     };
19228
19229     if(config && config.data){
19230         this.inlineData = config.data;
19231         delete config.data;
19232     }
19233
19234     Roo.apply(this, config);
19235     
19236     if(this.reader){ // reader passed
19237         this.reader = Roo.factory(this.reader, Roo.data);
19238         this.reader.xmodule = this.xmodule || false;
19239         if(!this.recordType){
19240             this.recordType = this.reader.recordType;
19241         }
19242         if(this.reader.onMetaChange){
19243             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19244         }
19245     }
19246
19247     if(this.recordType){
19248         this.fields = this.recordType.prototype.fields;
19249     }
19250     this.modified = [];
19251
19252     this.addEvents({
19253         /**
19254          * @event datachanged
19255          * Fires when the data cache has changed, and a widget which is using this Store
19256          * as a Record cache should refresh its view.
19257          * @param {Store} this
19258          */
19259         datachanged : true,
19260         /**
19261          * @event metachange
19262          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19263          * @param {Store} this
19264          * @param {Object} meta The JSON metadata
19265          */
19266         metachange : true,
19267         /**
19268          * @event add
19269          * Fires when Records have been added to the Store
19270          * @param {Store} this
19271          * @param {Roo.data.Record[]} records The array of Records added
19272          * @param {Number} index The index at which the record(s) were added
19273          */
19274         add : true,
19275         /**
19276          * @event remove
19277          * Fires when a Record has been removed from the Store
19278          * @param {Store} this
19279          * @param {Roo.data.Record} record The Record that was removed
19280          * @param {Number} index The index at which the record was removed
19281          */
19282         remove : true,
19283         /**
19284          * @event update
19285          * Fires when a Record has been updated
19286          * @param {Store} this
19287          * @param {Roo.data.Record} record The Record that was updated
19288          * @param {String} operation The update operation being performed.  Value may be one of:
19289          * <pre><code>
19290  Roo.data.Record.EDIT
19291  Roo.data.Record.REJECT
19292  Roo.data.Record.COMMIT
19293          * </code></pre>
19294          */
19295         update : true,
19296         /**
19297          * @event clear
19298          * Fires when the data cache has been cleared.
19299          * @param {Store} this
19300          */
19301         clear : true,
19302         /**
19303          * @event beforeload
19304          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19305          * the load action will be canceled.
19306          * @param {Store} this
19307          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19308          */
19309         beforeload : true,
19310         /**
19311          * @event load
19312          * Fires after a new set of Records has been loaded.
19313          * @param {Store} this
19314          * @param {Roo.data.Record[]} records The Records that were loaded
19315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19316          */
19317         load : true,
19318         /**
19319          * @event loadexception
19320          * Fires if an exception occurs in the Proxy during loading.
19321          * Called with the signature of the Proxy's "loadexception" event.
19322          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19323          * 
19324          * @param {Proxy} 
19325          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19326          * @param {Object} load options 
19327          * @param {Object} jsonData from your request (normally this contains the Exception)
19328          */
19329         loadexception : true
19330     });
19331     
19332     if(this.proxy){
19333         this.proxy = Roo.factory(this.proxy, Roo.data);
19334         this.proxy.xmodule = this.xmodule || false;
19335         this.relayEvents(this.proxy,  ["loadexception"]);
19336     }
19337     this.sortToggle = {};
19338     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19339
19340     Roo.data.Store.superclass.constructor.call(this);
19341
19342     if(this.inlineData){
19343         this.loadData(this.inlineData);
19344         delete this.inlineData;
19345     }
19346 };
19347 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19348      /**
19349     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19350     * without a remote query - used by combo/forms at present.
19351     */
19352     
19353     /**
19354     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19355     */
19356     /**
19357     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19358     */
19359     /**
19360     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19361     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19362     */
19363     /**
19364     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19365     * on any HTTP request
19366     */
19367     /**
19368     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19369     */
19370     /**
19371     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19372     */
19373     multiSort: false,
19374     /**
19375     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19376     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19377     */
19378     remoteSort : false,
19379
19380     /**
19381     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19382      * loaded or when a record is removed. (defaults to false).
19383     */
19384     pruneModifiedRecords : false,
19385
19386     // private
19387     lastOptions : null,
19388
19389     /**
19390      * Add Records to the Store and fires the add event.
19391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19392      */
19393     add : function(records){
19394         records = [].concat(records);
19395         for(var i = 0, len = records.length; i < len; i++){
19396             records[i].join(this);
19397         }
19398         var index = this.data.length;
19399         this.data.addAll(records);
19400         this.fireEvent("add", this, records, index);
19401     },
19402
19403     /**
19404      * Remove a Record from the Store and fires the remove event.
19405      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19406      */
19407     remove : function(record){
19408         var index = this.data.indexOf(record);
19409         this.data.removeAt(index);
19410         if(this.pruneModifiedRecords){
19411             this.modified.remove(record);
19412         }
19413         this.fireEvent("remove", this, record, index);
19414     },
19415
19416     /**
19417      * Remove all Records from the Store and fires the clear event.
19418      */
19419     removeAll : function(){
19420         this.data.clear();
19421         if(this.pruneModifiedRecords){
19422             this.modified = [];
19423         }
19424         this.fireEvent("clear", this);
19425     },
19426
19427     /**
19428      * Inserts Records to the Store at the given index and fires the add event.
19429      * @param {Number} index The start index at which to insert the passed Records.
19430      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19431      */
19432     insert : function(index, records){
19433         records = [].concat(records);
19434         for(var i = 0, len = records.length; i < len; i++){
19435             this.data.insert(index, records[i]);
19436             records[i].join(this);
19437         }
19438         this.fireEvent("add", this, records, index);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the passed Record.
19443      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19444      * @return {Number} The index of the passed Record. Returns -1 if not found.
19445      */
19446     indexOf : function(record){
19447         return this.data.indexOf(record);
19448     },
19449
19450     /**
19451      * Get the index within the cache of the Record with the passed id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Number} The index of the Record. Returns -1 if not found.
19454      */
19455     indexOfId : function(id){
19456         return this.data.indexOfKey(id);
19457     },
19458
19459     /**
19460      * Get the Record with the specified id.
19461      * @param {String} id The id of the Record to find.
19462      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19463      */
19464     getById : function(id){
19465         return this.data.key(id);
19466     },
19467
19468     /**
19469      * Get the Record at the specified index.
19470      * @param {Number} index The index of the Record to find.
19471      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19472      */
19473     getAt : function(index){
19474         return this.data.itemAt(index);
19475     },
19476
19477     /**
19478      * Returns a range of Records between specified indices.
19479      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19480      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19481      * @return {Roo.data.Record[]} An array of Records
19482      */
19483     getRange : function(start, end){
19484         return this.data.getRange(start, end);
19485     },
19486
19487     // private
19488     storeOptions : function(o){
19489         o = Roo.apply({}, o);
19490         delete o.callback;
19491         delete o.scope;
19492         this.lastOptions = o;
19493     },
19494
19495     /**
19496      * Loads the Record cache from the configured Proxy using the configured Reader.
19497      * <p>
19498      * If using remote paging, then the first load call must specify the <em>start</em>
19499      * and <em>limit</em> properties in the options.params property to establish the initial
19500      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19501      * <p>
19502      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19503      * and this call will return before the new data has been loaded. Perform any post-processing
19504      * in a callback function, or in a "load" event handler.</strong>
19505      * <p>
19506      * @param {Object} options An object containing properties which control loading options:<ul>
19507      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19508      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19509      * passed the following arguments:<ul>
19510      * <li>r : Roo.data.Record[]</li>
19511      * <li>options: Options object from the load call</li>
19512      * <li>success: Boolean success indicator</li></ul></li>
19513      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19514      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19515      * </ul>
19516      */
19517     load : function(options){
19518         options = options || {};
19519         if(this.fireEvent("beforeload", this, options) !== false){
19520             this.storeOptions(options);
19521             var p = Roo.apply(options.params || {}, this.baseParams);
19522             // if meta was not loaded from remote source.. try requesting it.
19523             if (!this.reader.metaFromRemote) {
19524                 p._requestMeta = 1;
19525             }
19526             if(this.sortInfo && this.remoteSort){
19527                 var pn = this.paramNames;
19528                 p[pn["sort"]] = this.sortInfo.field;
19529                 p[pn["dir"]] = this.sortInfo.direction;
19530             }
19531             if (this.multiSort) {
19532                 var pn = this.paramNames;
19533                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19534             }
19535             
19536             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19537         }
19538     },
19539
19540     /**
19541      * Reloads the Record cache from the configured Proxy using the configured Reader and
19542      * the options from the last load operation performed.
19543      * @param {Object} options (optional) An object containing properties which may override the options
19544      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19545      * the most recently used options are reused).
19546      */
19547     reload : function(options){
19548         this.load(Roo.applyIf(options||{}, this.lastOptions));
19549     },
19550
19551     // private
19552     // Called as a callback by the Reader during a load operation.
19553     loadRecords : function(o, options, success){
19554         if(!o || success === false){
19555             if(success !== false){
19556                 this.fireEvent("load", this, [], options);
19557             }
19558             if(options.callback){
19559                 options.callback.call(options.scope || this, [], options, false);
19560             }
19561             return;
19562         }
19563         // if data returned failure - throw an exception.
19564         if (o.success === false) {
19565             // show a message if no listener is registered.
19566             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19567                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19568             }
19569             // loadmask wil be hooked into this..
19570             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19571             return;
19572         }
19573         var r = o.records, t = o.totalRecords || r.length;
19574         if(!options || options.add !== true){
19575             if(this.pruneModifiedRecords){
19576                 this.modified = [];
19577             }
19578             for(var i = 0, len = r.length; i < len; i++){
19579                 r[i].join(this);
19580             }
19581             if(this.snapshot){
19582                 this.data = this.snapshot;
19583                 delete this.snapshot;
19584             }
19585             this.data.clear();
19586             this.data.addAll(r);
19587             this.totalLength = t;
19588             this.applySort();
19589             this.fireEvent("datachanged", this);
19590         }else{
19591             this.totalLength = Math.max(t, this.data.length+r.length);
19592             this.add(r);
19593         }
19594         this.fireEvent("load", this, r, options);
19595         if(options.callback){
19596             options.callback.call(options.scope || this, r, options, true);
19597         }
19598     },
19599
19600
19601     /**
19602      * Loads data from a passed data block. A Reader which understands the format of the data
19603      * must have been configured in the constructor.
19604      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19605      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19606      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19607      */
19608     loadData : function(o, append){
19609         var r = this.reader.readRecords(o);
19610         this.loadRecords(r, {add: append}, true);
19611     },
19612
19613     /**
19614      * Gets the number of cached records.
19615      * <p>
19616      * <em>If using paging, this may not be the total size of the dataset. If the data object
19617      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19618      * the data set size</em>
19619      */
19620     getCount : function(){
19621         return this.data.length || 0;
19622     },
19623
19624     /**
19625      * Gets the total number of records in the dataset as returned by the server.
19626      * <p>
19627      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19628      * the dataset size</em>
19629      */
19630     getTotalCount : function(){
19631         return this.totalLength || 0;
19632     },
19633
19634     /**
19635      * Returns the sort state of the Store as an object with two properties:
19636      * <pre><code>
19637  field {String} The name of the field by which the Records are sorted
19638  direction {String} The sort order, "ASC" or "DESC"
19639      * </code></pre>
19640      */
19641     getSortState : function(){
19642         return this.sortInfo;
19643     },
19644
19645     // private
19646     applySort : function(){
19647         if(this.sortInfo && !this.remoteSort){
19648             var s = this.sortInfo, f = s.field;
19649             var st = this.fields.get(f).sortType;
19650             var fn = function(r1, r2){
19651                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19652                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19653             };
19654             this.data.sort(s.direction, fn);
19655             if(this.snapshot && this.snapshot != this.data){
19656                 this.snapshot.sort(s.direction, fn);
19657             }
19658         }
19659     },
19660
19661     /**
19662      * Sets the default sort column and order to be used by the next load operation.
19663      * @param {String} fieldName The name of the field to sort by.
19664      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19665      */
19666     setDefaultSort : function(field, dir){
19667         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19668     },
19669
19670     /**
19671      * Sort the Records.
19672      * If remote sorting is used, the sort is performed on the server, and the cache is
19673      * reloaded. If local sorting is used, the cache is sorted internally.
19674      * @param {String} fieldName The name of the field to sort by.
19675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19676      */
19677     sort : function(fieldName, dir){
19678         var f = this.fields.get(fieldName);
19679         if(!dir){
19680             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19681             
19682             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19683                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19684             }else{
19685                 dir = f.sortDir;
19686             }
19687         }
19688         this.sortToggle[f.name] = dir;
19689         this.sortInfo = {field: f.name, direction: dir};
19690         if(!this.remoteSort){
19691             this.applySort();
19692             this.fireEvent("datachanged", this);
19693         }else{
19694             this.load(this.lastOptions);
19695         }
19696     },
19697
19698     /**
19699      * Calls the specified function for each of the Records in the cache.
19700      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19701      * Returning <em>false</em> aborts and exits the iteration.
19702      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19703      */
19704     each : function(fn, scope){
19705         this.data.each(fn, scope);
19706     },
19707
19708     /**
19709      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19710      * (e.g., during paging).
19711      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19712      */
19713     getModifiedRecords : function(){
19714         return this.modified;
19715     },
19716
19717     // private
19718     createFilterFn : function(property, value, anyMatch){
19719         if(!value.exec){ // not a regex
19720             value = String(value);
19721             if(value.length == 0){
19722                 return false;
19723             }
19724             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19725         }
19726         return function(r){
19727             return value.test(r.data[property]);
19728         };
19729     },
19730
19731     /**
19732      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19733      * @param {String} property A field on your records
19734      * @param {Number} start The record index to start at (defaults to 0)
19735      * @param {Number} end The last record index to include (defaults to length - 1)
19736      * @return {Number} The sum
19737      */
19738     sum : function(property, start, end){
19739         var rs = this.data.items, v = 0;
19740         start = start || 0;
19741         end = (end || end === 0) ? end : rs.length-1;
19742
19743         for(var i = start; i <= end; i++){
19744             v += (rs[i].data[property] || 0);
19745         }
19746         return v;
19747     },
19748
19749     /**
19750      * Filter the records by a specified property.
19751      * @param {String} field A field on your records
19752      * @param {String/RegExp} value Either a string that the field
19753      * should start with or a RegExp to test against the field
19754      * @param {Boolean} anyMatch True to match any part not just the beginning
19755      */
19756     filter : function(property, value, anyMatch){
19757         var fn = this.createFilterFn(property, value, anyMatch);
19758         return fn ? this.filterBy(fn) : this.clearFilter();
19759     },
19760
19761     /**
19762      * Filter by a function. The specified function will be called with each
19763      * record in this data source. If the function returns true the record is included,
19764      * otherwise it is filtered.
19765      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19766      * @param {Object} scope (optional) The scope of the function (defaults to this)
19767      */
19768     filterBy : function(fn, scope){
19769         this.snapshot = this.snapshot || this.data;
19770         this.data = this.queryBy(fn, scope||this);
19771         this.fireEvent("datachanged", this);
19772     },
19773
19774     /**
19775      * Query the records by a specified property.
19776      * @param {String} field A field on your records
19777      * @param {String/RegExp} value Either a string that the field
19778      * should start with or a RegExp to test against the field
19779      * @param {Boolean} anyMatch True to match any part not just the beginning
19780      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19781      */
19782     query : function(property, value, anyMatch){
19783         var fn = this.createFilterFn(property, value, anyMatch);
19784         return fn ? this.queryBy(fn) : this.data.clone();
19785     },
19786
19787     /**
19788      * Query by a function. The specified function will be called with each
19789      * record in this data source. If the function returns true the record is included
19790      * in the results.
19791      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19792      * @param {Object} scope (optional) The scope of the function (defaults to this)
19793       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19794      **/
19795     queryBy : function(fn, scope){
19796         var data = this.snapshot || this.data;
19797         return data.filterBy(fn, scope||this);
19798     },
19799
19800     /**
19801      * Collects unique values for a particular dataIndex from this store.
19802      * @param {String} dataIndex The property to collect
19803      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19804      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19805      * @return {Array} An array of the unique values
19806      **/
19807     collect : function(dataIndex, allowNull, bypassFilter){
19808         var d = (bypassFilter === true && this.snapshot) ?
19809                 this.snapshot.items : this.data.items;
19810         var v, sv, r = [], l = {};
19811         for(var i = 0, len = d.length; i < len; i++){
19812             v = d[i].data[dataIndex];
19813             sv = String(v);
19814             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19815                 l[sv] = true;
19816                 r[r.length] = v;
19817             }
19818         }
19819         return r;
19820     },
19821
19822     /**
19823      * Revert to a view of the Record cache with no filtering applied.
19824      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19825      */
19826     clearFilter : function(suppressEvent){
19827         if(this.snapshot && this.snapshot != this.data){
19828             this.data = this.snapshot;
19829             delete this.snapshot;
19830             if(suppressEvent !== true){
19831                 this.fireEvent("datachanged", this);
19832             }
19833         }
19834     },
19835
19836     // private
19837     afterEdit : function(record){
19838         if(this.modified.indexOf(record) == -1){
19839             this.modified.push(record);
19840         }
19841         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19842     },
19843     
19844     // private
19845     afterReject : function(record){
19846         this.modified.remove(record);
19847         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19848     },
19849
19850     // private
19851     afterCommit : function(record){
19852         this.modified.remove(record);
19853         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19854     },
19855
19856     /**
19857      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19858      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19859      */
19860     commitChanges : function(){
19861         var m = this.modified.slice(0);
19862         this.modified = [];
19863         for(var i = 0, len = m.length; i < len; i++){
19864             m[i].commit();
19865         }
19866     },
19867
19868     /**
19869      * Cancel outstanding changes on all changed records.
19870      */
19871     rejectChanges : function(){
19872         var m = this.modified.slice(0);
19873         this.modified = [];
19874         for(var i = 0, len = m.length; i < len; i++){
19875             m[i].reject();
19876         }
19877     },
19878
19879     onMetaChange : function(meta, rtype, o){
19880         this.recordType = rtype;
19881         this.fields = rtype.prototype.fields;
19882         delete this.snapshot;
19883         this.sortInfo = meta.sortInfo || this.sortInfo;
19884         this.modified = [];
19885         this.fireEvent('metachange', this, this.reader.meta);
19886     }
19887 });/*
19888  * Based on:
19889  * Ext JS Library 1.1.1
19890  * Copyright(c) 2006-2007, Ext JS, LLC.
19891  *
19892  * Originally Released Under LGPL - original licence link has changed is not relivant.
19893  *
19894  * Fork - LGPL
19895  * <script type="text/javascript">
19896  */
19897
19898 /**
19899  * @class Roo.data.SimpleStore
19900  * @extends Roo.data.Store
19901  * Small helper class to make creating Stores from Array data easier.
19902  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19903  * @cfg {Array} fields An array of field definition objects, or field name strings.
19904  * @cfg {Array} data The multi-dimensional array of data
19905  * @constructor
19906  * @param {Object} config
19907  */
19908 Roo.data.SimpleStore = function(config){
19909     Roo.data.SimpleStore.superclass.constructor.call(this, {
19910         isLocal : true,
19911         reader: new Roo.data.ArrayReader({
19912                 id: config.id
19913             },
19914             Roo.data.Record.create(config.fields)
19915         ),
19916         proxy : new Roo.data.MemoryProxy(config.data)
19917     });
19918     this.load();
19919 };
19920 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19921  * Based on:
19922  * Ext JS Library 1.1.1
19923  * Copyright(c) 2006-2007, Ext JS, LLC.
19924  *
19925  * Originally Released Under LGPL - original licence link has changed is not relivant.
19926  *
19927  * Fork - LGPL
19928  * <script type="text/javascript">
19929  */
19930
19931 /**
19932 /**
19933  * @extends Roo.data.Store
19934  * @class Roo.data.JsonStore
19935  * Small helper class to make creating Stores for JSON data easier. <br/>
19936 <pre><code>
19937 var store = new Roo.data.JsonStore({
19938     url: 'get-images.php',
19939     root: 'images',
19940     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19941 });
19942 </code></pre>
19943  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19944  * JsonReader and HttpProxy (unless inline data is provided).</b>
19945  * @cfg {Array} fields An array of field definition objects, or field name strings.
19946  * @constructor
19947  * @param {Object} config
19948  */
19949 Roo.data.JsonStore = function(c){
19950     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19951         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19952         reader: new Roo.data.JsonReader(c, c.fields)
19953     }));
19954 };
19955 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19956  * Based on:
19957  * Ext JS Library 1.1.1
19958  * Copyright(c) 2006-2007, Ext JS, LLC.
19959  *
19960  * Originally Released Under LGPL - original licence link has changed is not relivant.
19961  *
19962  * Fork - LGPL
19963  * <script type="text/javascript">
19964  */
19965
19966  
19967 Roo.data.Field = function(config){
19968     if(typeof config == "string"){
19969         config = {name: config};
19970     }
19971     Roo.apply(this, config);
19972     
19973     if(!this.type){
19974         this.type = "auto";
19975     }
19976     
19977     var st = Roo.data.SortTypes;
19978     // named sortTypes are supported, here we look them up
19979     if(typeof this.sortType == "string"){
19980         this.sortType = st[this.sortType];
19981     }
19982     
19983     // set default sortType for strings and dates
19984     if(!this.sortType){
19985         switch(this.type){
19986             case "string":
19987                 this.sortType = st.asUCString;
19988                 break;
19989             case "date":
19990                 this.sortType = st.asDate;
19991                 break;
19992             default:
19993                 this.sortType = st.none;
19994         }
19995     }
19996
19997     // define once
19998     var stripRe = /[\$,%]/g;
19999
20000     // prebuilt conversion function for this field, instead of
20001     // switching every time we're reading a value
20002     if(!this.convert){
20003         var cv, dateFormat = this.dateFormat;
20004         switch(this.type){
20005             case "":
20006             case "auto":
20007             case undefined:
20008                 cv = function(v){ return v; };
20009                 break;
20010             case "string":
20011                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20012                 break;
20013             case "int":
20014                 cv = function(v){
20015                     return v !== undefined && v !== null && v !== '' ?
20016                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20017                     };
20018                 break;
20019             case "float":
20020                 cv = function(v){
20021                     return v !== undefined && v !== null && v !== '' ?
20022                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20023                     };
20024                 break;
20025             case "bool":
20026             case "boolean":
20027                 cv = function(v){ return v === true || v === "true" || v == 1; };
20028                 break;
20029             case "date":
20030                 cv = function(v){
20031                     if(!v){
20032                         return '';
20033                     }
20034                     if(v instanceof Date){
20035                         return v;
20036                     }
20037                     if(dateFormat){
20038                         if(dateFormat == "timestamp"){
20039                             return new Date(v*1000);
20040                         }
20041                         return Date.parseDate(v, dateFormat);
20042                     }
20043                     var parsed = Date.parse(v);
20044                     return parsed ? new Date(parsed) : null;
20045                 };
20046              break;
20047             
20048         }
20049         this.convert = cv;
20050     }
20051 };
20052
20053 Roo.data.Field.prototype = {
20054     dateFormat: null,
20055     defaultValue: "",
20056     mapping: null,
20057     sortType : null,
20058     sortDir : "ASC"
20059 };/*
20060  * Based on:
20061  * Ext JS Library 1.1.1
20062  * Copyright(c) 2006-2007, Ext JS, LLC.
20063  *
20064  * Originally Released Under LGPL - original licence link has changed is not relivant.
20065  *
20066  * Fork - LGPL
20067  * <script type="text/javascript">
20068  */
20069  
20070 // Base class for reading structured data from a data source.  This class is intended to be
20071 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20072
20073 /**
20074  * @class Roo.data.DataReader
20075  * Base class for reading structured data from a data source.  This class is intended to be
20076  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20077  */
20078
20079 Roo.data.DataReader = function(meta, recordType){
20080     
20081     this.meta = meta;
20082     
20083     this.recordType = recordType instanceof Array ? 
20084         Roo.data.Record.create(recordType) : recordType;
20085 };
20086
20087 Roo.data.DataReader.prototype = {
20088      /**
20089      * Create an empty record
20090      * @param {Object} data (optional) - overlay some values
20091      * @return {Roo.data.Record} record created.
20092      */
20093     newRow :  function(d) {
20094         var da =  {};
20095         this.recordType.prototype.fields.each(function(c) {
20096             switch( c.type) {
20097                 case 'int' : da[c.name] = 0; break;
20098                 case 'date' : da[c.name] = new Date(); break;
20099                 case 'float' : da[c.name] = 0.0; break;
20100                 case 'boolean' : da[c.name] = false; break;
20101                 default : da[c.name] = ""; break;
20102             }
20103             
20104         });
20105         return new this.recordType(Roo.apply(da, d));
20106     }
20107     
20108 };/*
20109  * Based on:
20110  * Ext JS Library 1.1.1
20111  * Copyright(c) 2006-2007, Ext JS, LLC.
20112  *
20113  * Originally Released Under LGPL - original licence link has changed is not relivant.
20114  *
20115  * Fork - LGPL
20116  * <script type="text/javascript">
20117  */
20118
20119 /**
20120  * @class Roo.data.DataProxy
20121  * @extends Roo.data.Observable
20122  * This class is an abstract base class for implementations which provide retrieval of
20123  * unformatted data objects.<br>
20124  * <p>
20125  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20126  * (of the appropriate type which knows how to parse the data object) to provide a block of
20127  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20128  * <p>
20129  * Custom implementations must implement the load method as described in
20130  * {@link Roo.data.HttpProxy#load}.
20131  */
20132 Roo.data.DataProxy = function(){
20133     this.addEvents({
20134         /**
20135          * @event beforeload
20136          * Fires before a network request is made to retrieve a data object.
20137          * @param {Object} This DataProxy object.
20138          * @param {Object} params The params parameter to the load function.
20139          */
20140         beforeload : true,
20141         /**
20142          * @event load
20143          * Fires before the load method's callback is called.
20144          * @param {Object} This DataProxy object.
20145          * @param {Object} o The data object.
20146          * @param {Object} arg The callback argument object passed to the load function.
20147          */
20148         load : true,
20149         /**
20150          * @event loadexception
20151          * Fires if an Exception occurs during data retrieval.
20152          * @param {Object} This DataProxy object.
20153          * @param {Object} o The data object.
20154          * @param {Object} arg The callback argument object passed to the load function.
20155          * @param {Object} e The Exception.
20156          */
20157         loadexception : true
20158     });
20159     Roo.data.DataProxy.superclass.constructor.call(this);
20160 };
20161
20162 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20163
20164     /**
20165      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20166      */
20167 /*
20168  * Based on:
20169  * Ext JS Library 1.1.1
20170  * Copyright(c) 2006-2007, Ext JS, LLC.
20171  *
20172  * Originally Released Under LGPL - original licence link has changed is not relivant.
20173  *
20174  * Fork - LGPL
20175  * <script type="text/javascript">
20176  */
20177 /**
20178  * @class Roo.data.MemoryProxy
20179  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20180  * to the Reader when its load method is called.
20181  * @constructor
20182  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20183  */
20184 Roo.data.MemoryProxy = function(data){
20185     if (data.data) {
20186         data = data.data;
20187     }
20188     Roo.data.MemoryProxy.superclass.constructor.call(this);
20189     this.data = data;
20190 };
20191
20192 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20193     /**
20194      * Load data from the requested source (in this case an in-memory
20195      * data object passed to the constructor), read the data object into
20196      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20197      * process that block using the passed callback.
20198      * @param {Object} params This parameter is not used by the MemoryProxy class.
20199      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20200      * object into a block of Roo.data.Records.
20201      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20202      * The function must be passed <ul>
20203      * <li>The Record block object</li>
20204      * <li>The "arg" argument from the load function</li>
20205      * <li>A boolean success indicator</li>
20206      * </ul>
20207      * @param {Object} scope The scope in which to call the callback
20208      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20209      */
20210     load : function(params, reader, callback, scope, arg){
20211         params = params || {};
20212         var result;
20213         try {
20214             result = reader.readRecords(this.data);
20215         }catch(e){
20216             this.fireEvent("loadexception", this, arg, null, e);
20217             callback.call(scope, null, arg, false);
20218             return;
20219         }
20220         callback.call(scope, result, arg, true);
20221     },
20222     
20223     // private
20224     update : function(params, records){
20225         
20226     }
20227 });/*
20228  * Based on:
20229  * Ext JS Library 1.1.1
20230  * Copyright(c) 2006-2007, Ext JS, LLC.
20231  *
20232  * Originally Released Under LGPL - original licence link has changed is not relivant.
20233  *
20234  * Fork - LGPL
20235  * <script type="text/javascript">
20236  */
20237 /**
20238  * @class Roo.data.HttpProxy
20239  * @extends Roo.data.DataProxy
20240  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20241  * configured to reference a certain URL.<br><br>
20242  * <p>
20243  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20244  * from which the running page was served.<br><br>
20245  * <p>
20246  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20247  * <p>
20248  * Be aware that to enable the browser to parse an XML document, the server must set
20249  * the Content-Type header in the HTTP response to "text/xml".
20250  * @constructor
20251  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20252  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20253  * will be used to make the request.
20254  */
20255 Roo.data.HttpProxy = function(conn){
20256     Roo.data.HttpProxy.superclass.constructor.call(this);
20257     // is conn a conn config or a real conn?
20258     this.conn = conn;
20259     this.useAjax = !conn || !conn.events;
20260   
20261 };
20262
20263 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20264     // thse are take from connection...
20265     
20266     /**
20267      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20268      */
20269     /**
20270      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20271      * extra parameters to each request made by this object. (defaults to undefined)
20272      */
20273     /**
20274      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20275      *  to each request made by this object. (defaults to undefined)
20276      */
20277     /**
20278      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20279      */
20280     /**
20281      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20282      */
20283      /**
20284      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20285      * @type Boolean
20286      */
20287   
20288
20289     /**
20290      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20291      * @type Boolean
20292      */
20293     /**
20294      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20295      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20296      * a finer-grained basis than the DataProxy events.
20297      */
20298     getConnection : function(){
20299         return this.useAjax ? Roo.Ajax : this.conn;
20300     },
20301
20302     /**
20303      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20304      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20305      * process that block using the passed callback.
20306      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20307      * for the request to the remote server.
20308      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20309      * object into a block of Roo.data.Records.
20310      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20311      * The function must be passed <ul>
20312      * <li>The Record block object</li>
20313      * <li>The "arg" argument from the load function</li>
20314      * <li>A boolean success indicator</li>
20315      * </ul>
20316      * @param {Object} scope The scope in which to call the callback
20317      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20318      */
20319     load : function(params, reader, callback, scope, arg){
20320         if(this.fireEvent("beforeload", this, params) !== false){
20321             var  o = {
20322                 params : params || {},
20323                 request: {
20324                     callback : callback,
20325                     scope : scope,
20326                     arg : arg
20327                 },
20328                 reader: reader,
20329                 callback : this.loadResponse,
20330                 scope: this
20331             };
20332             if(this.useAjax){
20333                 Roo.applyIf(o, this.conn);
20334                 if(this.activeRequest){
20335                     Roo.Ajax.abort(this.activeRequest);
20336                 }
20337                 this.activeRequest = Roo.Ajax.request(o);
20338             }else{
20339                 this.conn.request(o);
20340             }
20341         }else{
20342             callback.call(scope||this, null, arg, false);
20343         }
20344     },
20345
20346     // private
20347     loadResponse : function(o, success, response){
20348         delete this.activeRequest;
20349         if(!success){
20350             this.fireEvent("loadexception", this, o, response);
20351             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20352             return;
20353         }
20354         var result;
20355         try {
20356             result = o.reader.read(response);
20357         }catch(e){
20358             this.fireEvent("loadexception", this, o, response, e);
20359             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20360             return;
20361         }
20362         
20363         this.fireEvent("load", this, o, o.request.arg);
20364         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20365     },
20366
20367     // private
20368     update : function(dataSet){
20369
20370     },
20371
20372     // private
20373     updateResponse : function(dataSet){
20374
20375     }
20376 });/*
20377  * Based on:
20378  * Ext JS Library 1.1.1
20379  * Copyright(c) 2006-2007, Ext JS, LLC.
20380  *
20381  * Originally Released Under LGPL - original licence link has changed is not relivant.
20382  *
20383  * Fork - LGPL
20384  * <script type="text/javascript">
20385  */
20386
20387 /**
20388  * @class Roo.data.ScriptTagProxy
20389  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20390  * other than the originating domain of the running page.<br><br>
20391  * <p>
20392  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20393  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20394  * <p>
20395  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20396  * source code that is used as the source inside a &lt;script> tag.<br><br>
20397  * <p>
20398  * In order for the browser to process the returned data, the server must wrap the data object
20399  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20400  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20401  * depending on whether the callback name was passed:
20402  * <p>
20403  * <pre><code>
20404 boolean scriptTag = false;
20405 String cb = request.getParameter("callback");
20406 if (cb != null) {
20407     scriptTag = true;
20408     response.setContentType("text/javascript");
20409 } else {
20410     response.setContentType("application/x-json");
20411 }
20412 Writer out = response.getWriter();
20413 if (scriptTag) {
20414     out.write(cb + "(");
20415 }
20416 out.print(dataBlock.toJsonString());
20417 if (scriptTag) {
20418     out.write(");");
20419 }
20420 </pre></code>
20421  *
20422  * @constructor
20423  * @param {Object} config A configuration object.
20424  */
20425 Roo.data.ScriptTagProxy = function(config){
20426     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20427     Roo.apply(this, config);
20428     this.head = document.getElementsByTagName("head")[0];
20429 };
20430
20431 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20432
20433 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20434     /**
20435      * @cfg {String} url The URL from which to request the data object.
20436      */
20437     /**
20438      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20439      */
20440     timeout : 30000,
20441     /**
20442      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20443      * the server the name of the callback function set up by the load call to process the returned data object.
20444      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20445      * javascript output which calls this named function passing the data object as its only parameter.
20446      */
20447     callbackParam : "callback",
20448     /**
20449      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20450      * name to the request.
20451      */
20452     nocache : true,
20453
20454     /**
20455      * Load data from the configured URL, read the data object into
20456      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20457      * process that block using the passed callback.
20458      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20459      * for the request to the remote server.
20460      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20461      * object into a block of Roo.data.Records.
20462      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20463      * The function must be passed <ul>
20464      * <li>The Record block object</li>
20465      * <li>The "arg" argument from the load function</li>
20466      * <li>A boolean success indicator</li>
20467      * </ul>
20468      * @param {Object} scope The scope in which to call the callback
20469      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20470      */
20471     load : function(params, reader, callback, scope, arg){
20472         if(this.fireEvent("beforeload", this, params) !== false){
20473
20474             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20475
20476             var url = this.url;
20477             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20478             if(this.nocache){
20479                 url += "&_dc=" + (new Date().getTime());
20480             }
20481             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20482             var trans = {
20483                 id : transId,
20484                 cb : "stcCallback"+transId,
20485                 scriptId : "stcScript"+transId,
20486                 params : params,
20487                 arg : arg,
20488                 url : url,
20489                 callback : callback,
20490                 scope : scope,
20491                 reader : reader
20492             };
20493             var conn = this;
20494
20495             window[trans.cb] = function(o){
20496                 conn.handleResponse(o, trans);
20497             };
20498
20499             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20500
20501             if(this.autoAbort !== false){
20502                 this.abort();
20503             }
20504
20505             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20506
20507             var script = document.createElement("script");
20508             script.setAttribute("src", url);
20509             script.setAttribute("type", "text/javascript");
20510             script.setAttribute("id", trans.scriptId);
20511             this.head.appendChild(script);
20512
20513             this.trans = trans;
20514         }else{
20515             callback.call(scope||this, null, arg, false);
20516         }
20517     },
20518
20519     // private
20520     isLoading : function(){
20521         return this.trans ? true : false;
20522     },
20523
20524     /**
20525      * Abort the current server request.
20526      */
20527     abort : function(){
20528         if(this.isLoading()){
20529             this.destroyTrans(this.trans);
20530         }
20531     },
20532
20533     // private
20534     destroyTrans : function(trans, isLoaded){
20535         this.head.removeChild(document.getElementById(trans.scriptId));
20536         clearTimeout(trans.timeoutId);
20537         if(isLoaded){
20538             window[trans.cb] = undefined;
20539             try{
20540                 delete window[trans.cb];
20541             }catch(e){}
20542         }else{
20543             // if hasn't been loaded, wait for load to remove it to prevent script error
20544             window[trans.cb] = function(){
20545                 window[trans.cb] = undefined;
20546                 try{
20547                     delete window[trans.cb];
20548                 }catch(e){}
20549             };
20550         }
20551     },
20552
20553     // private
20554     handleResponse : function(o, trans){
20555         this.trans = false;
20556         this.destroyTrans(trans, true);
20557         var result;
20558         try {
20559             result = trans.reader.readRecords(o);
20560         }catch(e){
20561             this.fireEvent("loadexception", this, o, trans.arg, e);
20562             trans.callback.call(trans.scope||window, null, trans.arg, false);
20563             return;
20564         }
20565         this.fireEvent("load", this, o, trans.arg);
20566         trans.callback.call(trans.scope||window, result, trans.arg, true);
20567     },
20568
20569     // private
20570     handleFailure : function(trans){
20571         this.trans = false;
20572         this.destroyTrans(trans, false);
20573         this.fireEvent("loadexception", this, null, trans.arg);
20574         trans.callback.call(trans.scope||window, null, trans.arg, false);
20575     }
20576 });/*
20577  * Based on:
20578  * Ext JS Library 1.1.1
20579  * Copyright(c) 2006-2007, Ext JS, LLC.
20580  *
20581  * Originally Released Under LGPL - original licence link has changed is not relivant.
20582  *
20583  * Fork - LGPL
20584  * <script type="text/javascript">
20585  */
20586
20587 /**
20588  * @class Roo.data.JsonReader
20589  * @extends Roo.data.DataReader
20590  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20591  * based on mappings in a provided Roo.data.Record constructor.
20592  * 
20593  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20594  * in the reply previously. 
20595  * 
20596  * <p>
20597  * Example code:
20598  * <pre><code>
20599 var RecordDef = Roo.data.Record.create([
20600     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20601     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20602 ]);
20603 var myReader = new Roo.data.JsonReader({
20604     totalProperty: "results",    // The property which contains the total dataset size (optional)
20605     root: "rows",                // The property which contains an Array of row objects
20606     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20607 }, RecordDef);
20608 </code></pre>
20609  * <p>
20610  * This would consume a JSON file like this:
20611  * <pre><code>
20612 { 'results': 2, 'rows': [
20613     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20614     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20615 }
20616 </code></pre>
20617  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20618  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20619  * paged from the remote server.
20620  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20621  * @cfg {String} root name of the property which contains the Array of row objects.
20622  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20623  * @constructor
20624  * Create a new JsonReader
20625  * @param {Object} meta Metadata configuration options
20626  * @param {Object} recordType Either an Array of field definition objects,
20627  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20628  */
20629 Roo.data.JsonReader = function(meta, recordType){
20630     
20631     meta = meta || {};
20632     // set some defaults:
20633     Roo.applyIf(meta, {
20634         totalProperty: 'total',
20635         successProperty : 'success',
20636         root : 'data',
20637         id : 'id'
20638     });
20639     
20640     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20641 };
20642 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20643     
20644     /**
20645      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20646      * Used by Store query builder to append _requestMeta to params.
20647      * 
20648      */
20649     metaFromRemote : false,
20650     /**
20651      * This method is only used by a DataProxy which has retrieved data from a remote server.
20652      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20653      * @return {Object} data A data block which is used by an Roo.data.Store object as
20654      * a cache of Roo.data.Records.
20655      */
20656     read : function(response){
20657         var json = response.responseText;
20658        
20659         var o = /* eval:var:o */ eval("("+json+")");
20660         if(!o) {
20661             throw {message: "JsonReader.read: Json object not found"};
20662         }
20663         
20664         if(o.metaData){
20665             
20666             delete this.ef;
20667             this.metaFromRemote = true;
20668             this.meta = o.metaData;
20669             this.recordType = Roo.data.Record.create(o.metaData.fields);
20670             this.onMetaChange(this.meta, this.recordType, o);
20671         }
20672         return this.readRecords(o);
20673     },
20674
20675     // private function a store will implement
20676     onMetaChange : function(meta, recordType, o){
20677
20678     },
20679
20680     /**
20681          * @ignore
20682          */
20683     simpleAccess: function(obj, subsc) {
20684         return obj[subsc];
20685     },
20686
20687         /**
20688          * @ignore
20689          */
20690     getJsonAccessor: function(){
20691         var re = /[\[\.]/;
20692         return function(expr) {
20693             try {
20694                 return(re.test(expr))
20695                     ? new Function("obj", "return obj." + expr)
20696                     : function(obj){
20697                         return obj[expr];
20698                     };
20699             } catch(e){}
20700             return Roo.emptyFn;
20701         };
20702     }(),
20703
20704     /**
20705      * Create a data block containing Roo.data.Records from an XML document.
20706      * @param {Object} o An object which contains an Array of row objects in the property specified
20707      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20708      * which contains the total size of the dataset.
20709      * @return {Object} data A data block which is used by an Roo.data.Store object as
20710      * a cache of Roo.data.Records.
20711      */
20712     readRecords : function(o){
20713         /**
20714          * After any data loads, the raw JSON data is available for further custom processing.
20715          * @type Object
20716          */
20717         this.jsonData = o;
20718         var s = this.meta, Record = this.recordType,
20719             f = Record.prototype.fields, fi = f.items, fl = f.length;
20720
20721 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20722         if (!this.ef) {
20723             if(s.totalProperty) {
20724                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20725                 }
20726                 if(s.successProperty) {
20727                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20728                 }
20729                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20730                 if (s.id) {
20731                         var g = this.getJsonAccessor(s.id);
20732                         this.getId = function(rec) {
20733                                 var r = g(rec);
20734                                 return (r === undefined || r === "") ? null : r;
20735                         };
20736                 } else {
20737                         this.getId = function(){return null;};
20738                 }
20739             this.ef = [];
20740             for(var jj = 0; jj < fl; jj++){
20741                 f = fi[jj];
20742                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20743                 this.ef[jj] = this.getJsonAccessor(map);
20744             }
20745         }
20746
20747         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20748         if(s.totalProperty){
20749             var vt = parseInt(this.getTotal(o), 10);
20750             if(!isNaN(vt)){
20751                 totalRecords = vt;
20752             }
20753         }
20754         if(s.successProperty){
20755             var vs = this.getSuccess(o);
20756             if(vs === false || vs === 'false'){
20757                 success = false;
20758             }
20759         }
20760         var records = [];
20761             for(var i = 0; i < c; i++){
20762                     var n = root[i];
20763                 var values = {};
20764                 var id = this.getId(n);
20765                 for(var j = 0; j < fl; j++){
20766                     f = fi[j];
20767                 var v = this.ef[j](n);
20768                 if (!f.convert) {
20769                     Roo.log('missing convert for ' + f.name);
20770                     Roo.log(f);
20771                     continue;
20772                 }
20773                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20774                 }
20775                 var record = new Record(values, id);
20776                 record.json = n;
20777                 records[i] = record;
20778             }
20779             return {
20780                 success : success,
20781                 records : records,
20782                 totalRecords : totalRecords
20783             };
20784     }
20785 });/*
20786  * Based on:
20787  * Ext JS Library 1.1.1
20788  * Copyright(c) 2006-2007, Ext JS, LLC.
20789  *
20790  * Originally Released Under LGPL - original licence link has changed is not relivant.
20791  *
20792  * Fork - LGPL
20793  * <script type="text/javascript">
20794  */
20795
20796 /**
20797  * @class Roo.data.XmlReader
20798  * @extends Roo.data.DataReader
20799  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20800  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20801  * <p>
20802  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20803  * header in the HTTP response must be set to "text/xml".</em>
20804  * <p>
20805  * Example code:
20806  * <pre><code>
20807 var RecordDef = Roo.data.Record.create([
20808    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20809    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20810 ]);
20811 var myReader = new Roo.data.XmlReader({
20812    totalRecords: "results", // The element which contains the total dataset size (optional)
20813    record: "row",           // The repeated element which contains row information
20814    id: "id"                 // The element within the row that provides an ID for the record (optional)
20815 }, RecordDef);
20816 </code></pre>
20817  * <p>
20818  * This would consume an XML file like this:
20819  * <pre><code>
20820 &lt;?xml?>
20821 &lt;dataset>
20822  &lt;results>2&lt;/results>
20823  &lt;row>
20824    &lt;id>1&lt;/id>
20825    &lt;name>Bill&lt;/name>
20826    &lt;occupation>Gardener&lt;/occupation>
20827  &lt;/row>
20828  &lt;row>
20829    &lt;id>2&lt;/id>
20830    &lt;name>Ben&lt;/name>
20831    &lt;occupation>Horticulturalist&lt;/occupation>
20832  &lt;/row>
20833 &lt;/dataset>
20834 </code></pre>
20835  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20836  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20837  * paged from the remote server.
20838  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20839  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20840  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20841  * a record identifier value.
20842  * @constructor
20843  * Create a new XmlReader
20844  * @param {Object} meta Metadata configuration options
20845  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20846  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20847  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20848  */
20849 Roo.data.XmlReader = function(meta, recordType){
20850     meta = meta || {};
20851     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20852 };
20853 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20854     /**
20855      * This method is only used by a DataProxy which has retrieved data from a remote server.
20856          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20857          * to contain a method called 'responseXML' that returns an XML document object.
20858      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20859      * a cache of Roo.data.Records.
20860      */
20861     read : function(response){
20862         var doc = response.responseXML;
20863         if(!doc) {
20864             throw {message: "XmlReader.read: XML Document not available"};
20865         }
20866         return this.readRecords(doc);
20867     },
20868
20869     /**
20870      * Create a data block containing Roo.data.Records from an XML document.
20871          * @param {Object} doc A parsed XML document.
20872      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20873      * a cache of Roo.data.Records.
20874      */
20875     readRecords : function(doc){
20876         /**
20877          * After any data loads/reads, the raw XML Document is available for further custom processing.
20878          * @type XMLDocument
20879          */
20880         this.xmlData = doc;
20881         var root = doc.documentElement || doc;
20882         var q = Roo.DomQuery;
20883         var recordType = this.recordType, fields = recordType.prototype.fields;
20884         var sid = this.meta.id;
20885         var totalRecords = 0, success = true;
20886         if(this.meta.totalRecords){
20887             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20888         }
20889         
20890         if(this.meta.success){
20891             var sv = q.selectValue(this.meta.success, root, true);
20892             success = sv !== false && sv !== 'false';
20893         }
20894         var records = [];
20895         var ns = q.select(this.meta.record, root);
20896         for(var i = 0, len = ns.length; i < len; i++) {
20897                 var n = ns[i];
20898                 var values = {};
20899                 var id = sid ? q.selectValue(sid, n) : undefined;
20900                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20901                     var f = fields.items[j];
20902                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20903                     v = f.convert(v);
20904                     values[f.name] = v;
20905                 }
20906                 var record = new recordType(values, id);
20907                 record.node = n;
20908                 records[records.length] = record;
20909             }
20910
20911             return {
20912                 success : success,
20913                 records : records,
20914                 totalRecords : totalRecords || records.length
20915             };
20916     }
20917 });/*
20918  * Based on:
20919  * Ext JS Library 1.1.1
20920  * Copyright(c) 2006-2007, Ext JS, LLC.
20921  *
20922  * Originally Released Under LGPL - original licence link has changed is not relivant.
20923  *
20924  * Fork - LGPL
20925  * <script type="text/javascript">
20926  */
20927
20928 /**
20929  * @class Roo.data.ArrayReader
20930  * @extends Roo.data.DataReader
20931  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20932  * Each element of that Array represents a row of data fields. The
20933  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20934  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20935  * <p>
20936  * Example code:.
20937  * <pre><code>
20938 var RecordDef = Roo.data.Record.create([
20939     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20940     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20941 ]);
20942 var myReader = new Roo.data.ArrayReader({
20943     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20944 }, RecordDef);
20945 </code></pre>
20946  * <p>
20947  * This would consume an Array like this:
20948  * <pre><code>
20949 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20950   </code></pre>
20951  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20952  * @constructor
20953  * Create a new JsonReader
20954  * @param {Object} meta Metadata configuration options.
20955  * @param {Object} recordType Either an Array of field definition objects
20956  * as specified to {@link Roo.data.Record#create},
20957  * or an {@link Roo.data.Record} object
20958  * created using {@link Roo.data.Record#create}.
20959  */
20960 Roo.data.ArrayReader = function(meta, recordType){
20961     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20962 };
20963
20964 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20965     /**
20966      * Create a data block containing Roo.data.Records from an XML document.
20967      * @param {Object} o An Array of row objects which represents the dataset.
20968      * @return {Object} data A data block which is used by an Roo.data.Store object as
20969      * a cache of Roo.data.Records.
20970      */
20971     readRecords : function(o){
20972         var sid = this.meta ? this.meta.id : null;
20973         var recordType = this.recordType, fields = recordType.prototype.fields;
20974         var records = [];
20975         var root = o;
20976             for(var i = 0; i < root.length; i++){
20977                     var n = root[i];
20978                 var values = {};
20979                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20980                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20981                 var f = fields.items[j];
20982                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20983                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20984                 v = f.convert(v);
20985                 values[f.name] = v;
20986             }
20987                 var record = new recordType(values, id);
20988                 record.json = n;
20989                 records[records.length] = record;
20990             }
20991             return {
20992                 records : records,
20993                 totalRecords : records.length
20994             };
20995     }
20996 });/*
20997  * Based on:
20998  * Ext JS Library 1.1.1
20999  * Copyright(c) 2006-2007, Ext JS, LLC.
21000  *
21001  * Originally Released Under LGPL - original licence link has changed is not relivant.
21002  *
21003  * Fork - LGPL
21004  * <script type="text/javascript">
21005  */
21006
21007
21008 /**
21009  * @class Roo.data.Tree
21010  * @extends Roo.util.Observable
21011  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21012  * in the tree have most standard DOM functionality.
21013  * @constructor
21014  * @param {Node} root (optional) The root node
21015  */
21016 Roo.data.Tree = function(root){
21017    this.nodeHash = {};
21018    /**
21019     * The root node for this tree
21020     * @type Node
21021     */
21022    this.root = null;
21023    if(root){
21024        this.setRootNode(root);
21025    }
21026    this.addEvents({
21027        /**
21028         * @event append
21029         * Fires when a new child node is appended to a node in this tree.
21030         * @param {Tree} tree The owner tree
21031         * @param {Node} parent The parent node
21032         * @param {Node} node The newly appended node
21033         * @param {Number} index The index of the newly appended node
21034         */
21035        "append" : true,
21036        /**
21037         * @event remove
21038         * Fires when a child node is removed from a node in this tree.
21039         * @param {Tree} tree The owner tree
21040         * @param {Node} parent The parent node
21041         * @param {Node} node The child node removed
21042         */
21043        "remove" : true,
21044        /**
21045         * @event move
21046         * Fires when a node is moved to a new location in the tree
21047         * @param {Tree} tree The owner tree
21048         * @param {Node} node The node moved
21049         * @param {Node} oldParent The old parent of this node
21050         * @param {Node} newParent The new parent of this node
21051         * @param {Number} index The index it was moved to
21052         */
21053        "move" : true,
21054        /**
21055         * @event insert
21056         * Fires when a new child node is inserted in a node in this tree.
21057         * @param {Tree} tree The owner tree
21058         * @param {Node} parent The parent node
21059         * @param {Node} node The child node inserted
21060         * @param {Node} refNode The child node the node was inserted before
21061         */
21062        "insert" : true,
21063        /**
21064         * @event beforeappend
21065         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21066         * @param {Tree} tree The owner tree
21067         * @param {Node} parent The parent node
21068         * @param {Node} node The child node to be appended
21069         */
21070        "beforeappend" : true,
21071        /**
21072         * @event beforeremove
21073         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21074         * @param {Tree} tree The owner tree
21075         * @param {Node} parent The parent node
21076         * @param {Node} node The child node to be removed
21077         */
21078        "beforeremove" : true,
21079        /**
21080         * @event beforemove
21081         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21082         * @param {Tree} tree The owner tree
21083         * @param {Node} node The node being moved
21084         * @param {Node} oldParent The parent of the node
21085         * @param {Node} newParent The new parent the node is moving to
21086         * @param {Number} index The index it is being moved to
21087         */
21088        "beforemove" : true,
21089        /**
21090         * @event beforeinsert
21091         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21092         * @param {Tree} tree The owner tree
21093         * @param {Node} parent The parent node
21094         * @param {Node} node The child node to be inserted
21095         * @param {Node} refNode The child node the node is being inserted before
21096         */
21097        "beforeinsert" : true
21098    });
21099
21100     Roo.data.Tree.superclass.constructor.call(this);
21101 };
21102
21103 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21104     pathSeparator: "/",
21105
21106     proxyNodeEvent : function(){
21107         return this.fireEvent.apply(this, arguments);
21108     },
21109
21110     /**
21111      * Returns the root node for this tree.
21112      * @return {Node}
21113      */
21114     getRootNode : function(){
21115         return this.root;
21116     },
21117
21118     /**
21119      * Sets the root node for this tree.
21120      * @param {Node} node
21121      * @return {Node}
21122      */
21123     setRootNode : function(node){
21124         this.root = node;
21125         node.ownerTree = this;
21126         node.isRoot = true;
21127         this.registerNode(node);
21128         return node;
21129     },
21130
21131     /**
21132      * Gets a node in this tree by its id.
21133      * @param {String} id
21134      * @return {Node}
21135      */
21136     getNodeById : function(id){
21137         return this.nodeHash[id];
21138     },
21139
21140     registerNode : function(node){
21141         this.nodeHash[node.id] = node;
21142     },
21143
21144     unregisterNode : function(node){
21145         delete this.nodeHash[node.id];
21146     },
21147
21148     toString : function(){
21149         return "[Tree"+(this.id?" "+this.id:"")+"]";
21150     }
21151 });
21152
21153 /**
21154  * @class Roo.data.Node
21155  * @extends Roo.util.Observable
21156  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21157  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21158  * @constructor
21159  * @param {Object} attributes The attributes/config for the node
21160  */
21161 Roo.data.Node = function(attributes){
21162     /**
21163      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21164      * @type {Object}
21165      */
21166     this.attributes = attributes || {};
21167     this.leaf = this.attributes.leaf;
21168     /**
21169      * The node id. @type String
21170      */
21171     this.id = this.attributes.id;
21172     if(!this.id){
21173         this.id = Roo.id(null, "ynode-");
21174         this.attributes.id = this.id;
21175     }
21176     /**
21177      * All child nodes of this node. @type Array
21178      */
21179     this.childNodes = [];
21180     if(!this.childNodes.indexOf){ // indexOf is a must
21181         this.childNodes.indexOf = function(o){
21182             for(var i = 0, len = this.length; i < len; i++){
21183                 if(this[i] == o) {
21184                     return i;
21185                 }
21186             }
21187             return -1;
21188         };
21189     }
21190     /**
21191      * The parent node for this node. @type Node
21192      */
21193     this.parentNode = null;
21194     /**
21195      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21196      */
21197     this.firstChild = null;
21198     /**
21199      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21200      */
21201     this.lastChild = null;
21202     /**
21203      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21204      */
21205     this.previousSibling = null;
21206     /**
21207      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21208      */
21209     this.nextSibling = null;
21210
21211     this.addEvents({
21212        /**
21213         * @event append
21214         * Fires when a new child node is appended
21215         * @param {Tree} tree The owner tree
21216         * @param {Node} this This node
21217         * @param {Node} node The newly appended node
21218         * @param {Number} index The index of the newly appended node
21219         */
21220        "append" : true,
21221        /**
21222         * @event remove
21223         * Fires when a child node is removed
21224         * @param {Tree} tree The owner tree
21225         * @param {Node} this This node
21226         * @param {Node} node The removed node
21227         */
21228        "remove" : true,
21229        /**
21230         * @event move
21231         * Fires when this node is moved to a new location in the tree
21232         * @param {Tree} tree The owner tree
21233         * @param {Node} this This node
21234         * @param {Node} oldParent The old parent of this node
21235         * @param {Node} newParent The new parent of this node
21236         * @param {Number} index The index it was moved to
21237         */
21238        "move" : true,
21239        /**
21240         * @event insert
21241         * Fires when a new child node is inserted.
21242         * @param {Tree} tree The owner tree
21243         * @param {Node} this This node
21244         * @param {Node} node The child node inserted
21245         * @param {Node} refNode The child node the node was inserted before
21246         */
21247        "insert" : true,
21248        /**
21249         * @event beforeappend
21250         * Fires before a new child is appended, return false to cancel the append.
21251         * @param {Tree} tree The owner tree
21252         * @param {Node} this This node
21253         * @param {Node} node The child node to be appended
21254         */
21255        "beforeappend" : true,
21256        /**
21257         * @event beforeremove
21258         * Fires before a child is removed, return false to cancel the remove.
21259         * @param {Tree} tree The owner tree
21260         * @param {Node} this This node
21261         * @param {Node} node The child node to be removed
21262         */
21263        "beforeremove" : true,
21264        /**
21265         * @event beforemove
21266         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21267         * @param {Tree} tree The owner tree
21268         * @param {Node} this This node
21269         * @param {Node} oldParent The parent of this node
21270         * @param {Node} newParent The new parent this node is moving to
21271         * @param {Number} index The index it is being moved to
21272         */
21273        "beforemove" : true,
21274        /**
21275         * @event beforeinsert
21276         * Fires before a new child is inserted, return false to cancel the insert.
21277         * @param {Tree} tree The owner tree
21278         * @param {Node} this This node
21279         * @param {Node} node The child node to be inserted
21280         * @param {Node} refNode The child node the node is being inserted before
21281         */
21282        "beforeinsert" : true
21283    });
21284     this.listeners = this.attributes.listeners;
21285     Roo.data.Node.superclass.constructor.call(this);
21286 };
21287
21288 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21289     fireEvent : function(evtName){
21290         // first do standard event for this node
21291         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21292             return false;
21293         }
21294         // then bubble it up to the tree if the event wasn't cancelled
21295         var ot = this.getOwnerTree();
21296         if(ot){
21297             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21298                 return false;
21299             }
21300         }
21301         return true;
21302     },
21303
21304     /**
21305      * Returns true if this node is a leaf
21306      * @return {Boolean}
21307      */
21308     isLeaf : function(){
21309         return this.leaf === true;
21310     },
21311
21312     // private
21313     setFirstChild : function(node){
21314         this.firstChild = node;
21315     },
21316
21317     //private
21318     setLastChild : function(node){
21319         this.lastChild = node;
21320     },
21321
21322
21323     /**
21324      * Returns true if this node is the last child of its parent
21325      * @return {Boolean}
21326      */
21327     isLast : function(){
21328        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21329     },
21330
21331     /**
21332      * Returns true if this node is the first child of its parent
21333      * @return {Boolean}
21334      */
21335     isFirst : function(){
21336        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21337     },
21338
21339     hasChildNodes : function(){
21340         return !this.isLeaf() && this.childNodes.length > 0;
21341     },
21342
21343     /**
21344      * Insert node(s) as the last child node of this node.
21345      * @param {Node/Array} node The node or Array of nodes to append
21346      * @return {Node} The appended node if single append, or null if an array was passed
21347      */
21348     appendChild : function(node){
21349         var multi = false;
21350         if(node instanceof Array){
21351             multi = node;
21352         }else if(arguments.length > 1){
21353             multi = arguments;
21354         }
21355         // if passed an array or multiple args do them one by one
21356         if(multi){
21357             for(var i = 0, len = multi.length; i < len; i++) {
21358                 this.appendChild(multi[i]);
21359             }
21360         }else{
21361             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21362                 return false;
21363             }
21364             var index = this.childNodes.length;
21365             var oldParent = node.parentNode;
21366             // it's a move, make sure we move it cleanly
21367             if(oldParent){
21368                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21369                     return false;
21370                 }
21371                 oldParent.removeChild(node);
21372             }
21373             index = this.childNodes.length;
21374             if(index == 0){
21375                 this.setFirstChild(node);
21376             }
21377             this.childNodes.push(node);
21378             node.parentNode = this;
21379             var ps = this.childNodes[index-1];
21380             if(ps){
21381                 node.previousSibling = ps;
21382                 ps.nextSibling = node;
21383             }else{
21384                 node.previousSibling = null;
21385             }
21386             node.nextSibling = null;
21387             this.setLastChild(node);
21388             node.setOwnerTree(this.getOwnerTree());
21389             this.fireEvent("append", this.ownerTree, this, node, index);
21390             if(oldParent){
21391                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21392             }
21393             return node;
21394         }
21395     },
21396
21397     /**
21398      * Removes a child node from this node.
21399      * @param {Node} node The node to remove
21400      * @return {Node} The removed node
21401      */
21402     removeChild : function(node){
21403         var index = this.childNodes.indexOf(node);
21404         if(index == -1){
21405             return false;
21406         }
21407         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21408             return false;
21409         }
21410
21411         // remove it from childNodes collection
21412         this.childNodes.splice(index, 1);
21413
21414         // update siblings
21415         if(node.previousSibling){
21416             node.previousSibling.nextSibling = node.nextSibling;
21417         }
21418         if(node.nextSibling){
21419             node.nextSibling.previousSibling = node.previousSibling;
21420         }
21421
21422         // update child refs
21423         if(this.firstChild == node){
21424             this.setFirstChild(node.nextSibling);
21425         }
21426         if(this.lastChild == node){
21427             this.setLastChild(node.previousSibling);
21428         }
21429
21430         node.setOwnerTree(null);
21431         // clear any references from the node
21432         node.parentNode = null;
21433         node.previousSibling = null;
21434         node.nextSibling = null;
21435         this.fireEvent("remove", this.ownerTree, this, node);
21436         return node;
21437     },
21438
21439     /**
21440      * Inserts the first node before the second node in this nodes childNodes collection.
21441      * @param {Node} node The node to insert
21442      * @param {Node} refNode The node to insert before (if null the node is appended)
21443      * @return {Node} The inserted node
21444      */
21445     insertBefore : function(node, refNode){
21446         if(!refNode){ // like standard Dom, refNode can be null for append
21447             return this.appendChild(node);
21448         }
21449         // nothing to do
21450         if(node == refNode){
21451             return false;
21452         }
21453
21454         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21455             return false;
21456         }
21457         var index = this.childNodes.indexOf(refNode);
21458         var oldParent = node.parentNode;
21459         var refIndex = index;
21460
21461         // when moving internally, indexes will change after remove
21462         if(oldParent == this && this.childNodes.indexOf(node) < index){
21463             refIndex--;
21464         }
21465
21466         // it's a move, make sure we move it cleanly
21467         if(oldParent){
21468             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21469                 return false;
21470             }
21471             oldParent.removeChild(node);
21472         }
21473         if(refIndex == 0){
21474             this.setFirstChild(node);
21475         }
21476         this.childNodes.splice(refIndex, 0, node);
21477         node.parentNode = this;
21478         var ps = this.childNodes[refIndex-1];
21479         if(ps){
21480             node.previousSibling = ps;
21481             ps.nextSibling = node;
21482         }else{
21483             node.previousSibling = null;
21484         }
21485         node.nextSibling = refNode;
21486         refNode.previousSibling = node;
21487         node.setOwnerTree(this.getOwnerTree());
21488         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21489         if(oldParent){
21490             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21491         }
21492         return node;
21493     },
21494
21495     /**
21496      * Returns the child node at the specified index.
21497      * @param {Number} index
21498      * @return {Node}
21499      */
21500     item : function(index){
21501         return this.childNodes[index];
21502     },
21503
21504     /**
21505      * Replaces one child node in this node with another.
21506      * @param {Node} newChild The replacement node
21507      * @param {Node} oldChild The node to replace
21508      * @return {Node} The replaced node
21509      */
21510     replaceChild : function(newChild, oldChild){
21511         this.insertBefore(newChild, oldChild);
21512         this.removeChild(oldChild);
21513         return oldChild;
21514     },
21515
21516     /**
21517      * Returns the index of a child node
21518      * @param {Node} node
21519      * @return {Number} The index of the node or -1 if it was not found
21520      */
21521     indexOf : function(child){
21522         return this.childNodes.indexOf(child);
21523     },
21524
21525     /**
21526      * Returns the tree this node is in.
21527      * @return {Tree}
21528      */
21529     getOwnerTree : function(){
21530         // if it doesn't have one, look for one
21531         if(!this.ownerTree){
21532             var p = this;
21533             while(p){
21534                 if(p.ownerTree){
21535                     this.ownerTree = p.ownerTree;
21536                     break;
21537                 }
21538                 p = p.parentNode;
21539             }
21540         }
21541         return this.ownerTree;
21542     },
21543
21544     /**
21545      * Returns depth of this node (the root node has a depth of 0)
21546      * @return {Number}
21547      */
21548     getDepth : function(){
21549         var depth = 0;
21550         var p = this;
21551         while(p.parentNode){
21552             ++depth;
21553             p = p.parentNode;
21554         }
21555         return depth;
21556     },
21557
21558     // private
21559     setOwnerTree : function(tree){
21560         // if it's move, we need to update everyone
21561         if(tree != this.ownerTree){
21562             if(this.ownerTree){
21563                 this.ownerTree.unregisterNode(this);
21564             }
21565             this.ownerTree = tree;
21566             var cs = this.childNodes;
21567             for(var i = 0, len = cs.length; i < len; i++) {
21568                 cs[i].setOwnerTree(tree);
21569             }
21570             if(tree){
21571                 tree.registerNode(this);
21572             }
21573         }
21574     },
21575
21576     /**
21577      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21578      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21579      * @return {String} The path
21580      */
21581     getPath : function(attr){
21582         attr = attr || "id";
21583         var p = this.parentNode;
21584         var b = [this.attributes[attr]];
21585         while(p){
21586             b.unshift(p.attributes[attr]);
21587             p = p.parentNode;
21588         }
21589         var sep = this.getOwnerTree().pathSeparator;
21590         return sep + b.join(sep);
21591     },
21592
21593     /**
21594      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21595      * function call will be the scope provided or the current node. The arguments to the function
21596      * will be the args provided or the current node. If the function returns false at any point,
21597      * the bubble is stopped.
21598      * @param {Function} fn The function to call
21599      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21600      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21601      */
21602     bubble : function(fn, scope, args){
21603         var p = this;
21604         while(p){
21605             if(fn.call(scope || p, args || p) === false){
21606                 break;
21607             }
21608             p = p.parentNode;
21609         }
21610     },
21611
21612     /**
21613      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21614      * function call will be the scope provided or the current node. The arguments to the function
21615      * will be the args provided or the current node. If the function returns false at any point,
21616      * the cascade is stopped on that branch.
21617      * @param {Function} fn The function to call
21618      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21619      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21620      */
21621     cascade : function(fn, scope, args){
21622         if(fn.call(scope || this, args || this) !== false){
21623             var cs = this.childNodes;
21624             for(var i = 0, len = cs.length; i < len; i++) {
21625                 cs[i].cascade(fn, scope, args);
21626             }
21627         }
21628     },
21629
21630     /**
21631      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21632      * function call will be the scope provided or the current node. The arguments to the function
21633      * will be the args provided or the current node. If the function returns false at any point,
21634      * the iteration stops.
21635      * @param {Function} fn The function to call
21636      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21637      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21638      */
21639     eachChild : function(fn, scope, args){
21640         var cs = this.childNodes;
21641         for(var i = 0, len = cs.length; i < len; i++) {
21642                 if(fn.call(scope || this, args || cs[i]) === false){
21643                     break;
21644                 }
21645         }
21646     },
21647
21648     /**
21649      * Finds the first child that has the attribute with the specified value.
21650      * @param {String} attribute The attribute name
21651      * @param {Mixed} value The value to search for
21652      * @return {Node} The found child or null if none was found
21653      */
21654     findChild : function(attribute, value){
21655         var cs = this.childNodes;
21656         for(var i = 0, len = cs.length; i < len; i++) {
21657                 if(cs[i].attributes[attribute] == value){
21658                     return cs[i];
21659                 }
21660         }
21661         return null;
21662     },
21663
21664     /**
21665      * Finds the first child by a custom function. The child matches if the function passed
21666      * returns true.
21667      * @param {Function} fn
21668      * @param {Object} scope (optional)
21669      * @return {Node} The found child or null if none was found
21670      */
21671     findChildBy : function(fn, scope){
21672         var cs = this.childNodes;
21673         for(var i = 0, len = cs.length; i < len; i++) {
21674                 if(fn.call(scope||cs[i], cs[i]) === true){
21675                     return cs[i];
21676                 }
21677         }
21678         return null;
21679     },
21680
21681     /**
21682      * Sorts this nodes children using the supplied sort function
21683      * @param {Function} fn
21684      * @param {Object} scope (optional)
21685      */
21686     sort : function(fn, scope){
21687         var cs = this.childNodes;
21688         var len = cs.length;
21689         if(len > 0){
21690             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21691             cs.sort(sortFn);
21692             for(var i = 0; i < len; i++){
21693                 var n = cs[i];
21694                 n.previousSibling = cs[i-1];
21695                 n.nextSibling = cs[i+1];
21696                 if(i == 0){
21697                     this.setFirstChild(n);
21698                 }
21699                 if(i == len-1){
21700                     this.setLastChild(n);
21701                 }
21702             }
21703         }
21704     },
21705
21706     /**
21707      * Returns true if this node is an ancestor (at any point) of the passed node.
21708      * @param {Node} node
21709      * @return {Boolean}
21710      */
21711     contains : function(node){
21712         return node.isAncestor(this);
21713     },
21714
21715     /**
21716      * Returns true if the passed node is an ancestor (at any point) of this node.
21717      * @param {Node} node
21718      * @return {Boolean}
21719      */
21720     isAncestor : function(node){
21721         var p = this.parentNode;
21722         while(p){
21723             if(p == node){
21724                 return true;
21725             }
21726             p = p.parentNode;
21727         }
21728         return false;
21729     },
21730
21731     toString : function(){
21732         return "[Node"+(this.id?" "+this.id:"")+"]";
21733     }
21734 });/*
21735  * Based on:
21736  * Ext JS Library 1.1.1
21737  * Copyright(c) 2006-2007, Ext JS, LLC.
21738  *
21739  * Originally Released Under LGPL - original licence link has changed is not relivant.
21740  *
21741  * Fork - LGPL
21742  * <script type="text/javascript">
21743  */
21744  
21745
21746 /**
21747  * @class Roo.ComponentMgr
21748  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21749  * @singleton
21750  */
21751 Roo.ComponentMgr = function(){
21752     var all = new Roo.util.MixedCollection();
21753
21754     return {
21755         /**
21756          * Registers a component.
21757          * @param {Roo.Component} c The component
21758          */
21759         register : function(c){
21760             all.add(c);
21761         },
21762
21763         /**
21764          * Unregisters a component.
21765          * @param {Roo.Component} c The component
21766          */
21767         unregister : function(c){
21768             all.remove(c);
21769         },
21770
21771         /**
21772          * Returns a component by id
21773          * @param {String} id The component id
21774          */
21775         get : function(id){
21776             return all.get(id);
21777         },
21778
21779         /**
21780          * Registers a function that will be called when a specified component is added to ComponentMgr
21781          * @param {String} id The component id
21782          * @param {Funtction} fn The callback function
21783          * @param {Object} scope The scope of the callback
21784          */
21785         onAvailable : function(id, fn, scope){
21786             all.on("add", function(index, o){
21787                 if(o.id == id){
21788                     fn.call(scope || o, o);
21789                     all.un("add", fn, scope);
21790                 }
21791             });
21792         }
21793     };
21794 }();/*
21795  * Based on:
21796  * Ext JS Library 1.1.1
21797  * Copyright(c) 2006-2007, Ext JS, LLC.
21798  *
21799  * Originally Released Under LGPL - original licence link has changed is not relivant.
21800  *
21801  * Fork - LGPL
21802  * <script type="text/javascript">
21803  */
21804  
21805 /**
21806  * @class Roo.Component
21807  * @extends Roo.util.Observable
21808  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21809  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21810  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21811  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21812  * All visual components (widgets) that require rendering into a layout should subclass Component.
21813  * @constructor
21814  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21815  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21816  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21817  */
21818 Roo.Component = function(config){
21819     config = config || {};
21820     if(config.tagName || config.dom || typeof config == "string"){ // element object
21821         config = {el: config, id: config.id || config};
21822     }
21823     this.initialConfig = config;
21824
21825     Roo.apply(this, config);
21826     this.addEvents({
21827         /**
21828          * @event disable
21829          * Fires after the component is disabled.
21830              * @param {Roo.Component} this
21831              */
21832         disable : true,
21833         /**
21834          * @event enable
21835          * Fires after the component is enabled.
21836              * @param {Roo.Component} this
21837              */
21838         enable : true,
21839         /**
21840          * @event beforeshow
21841          * Fires before the component is shown.  Return false to stop the show.
21842              * @param {Roo.Component} this
21843              */
21844         beforeshow : true,
21845         /**
21846          * @event show
21847          * Fires after the component is shown.
21848              * @param {Roo.Component} this
21849              */
21850         show : true,
21851         /**
21852          * @event beforehide
21853          * Fires before the component is hidden. Return false to stop the hide.
21854              * @param {Roo.Component} this
21855              */
21856         beforehide : true,
21857         /**
21858          * @event hide
21859          * Fires after the component is hidden.
21860              * @param {Roo.Component} this
21861              */
21862         hide : true,
21863         /**
21864          * @event beforerender
21865          * Fires before the component is rendered. Return false to stop the render.
21866              * @param {Roo.Component} this
21867              */
21868         beforerender : true,
21869         /**
21870          * @event render
21871          * Fires after the component is rendered.
21872              * @param {Roo.Component} this
21873              */
21874         render : true,
21875         /**
21876          * @event beforedestroy
21877          * Fires before the component is destroyed. Return false to stop the destroy.
21878              * @param {Roo.Component} this
21879              */
21880         beforedestroy : true,
21881         /**
21882          * @event destroy
21883          * Fires after the component is destroyed.
21884              * @param {Roo.Component} this
21885              */
21886         destroy : true
21887     });
21888     if(!this.id){
21889         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21890     }
21891     Roo.ComponentMgr.register(this);
21892     Roo.Component.superclass.constructor.call(this);
21893     this.initComponent();
21894     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21895         this.render(this.renderTo);
21896         delete this.renderTo;
21897     }
21898 };
21899
21900 /** @private */
21901 Roo.Component.AUTO_ID = 1000;
21902
21903 Roo.extend(Roo.Component, Roo.util.Observable, {
21904     /**
21905      * @scope Roo.Component.prototype
21906      * @type {Boolean}
21907      * true if this component is hidden. Read-only.
21908      */
21909     hidden : false,
21910     /**
21911      * @type {Boolean}
21912      * true if this component is disabled. Read-only.
21913      */
21914     disabled : false,
21915     /**
21916      * @type {Boolean}
21917      * true if this component has been rendered. Read-only.
21918      */
21919     rendered : false,
21920     
21921     /** @cfg {String} disableClass
21922      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21923      */
21924     disabledClass : "x-item-disabled",
21925         /** @cfg {Boolean} allowDomMove
21926          * Whether the component can move the Dom node when rendering (defaults to true).
21927          */
21928     allowDomMove : true,
21929     /** @cfg {String} hideMode
21930      * How this component should hidden. Supported values are
21931      * "visibility" (css visibility), "offsets" (negative offset position) and
21932      * "display" (css display) - defaults to "display".
21933      */
21934     hideMode: 'display',
21935
21936     /** @private */
21937     ctype : "Roo.Component",
21938
21939     /**
21940      * @cfg {String} actionMode 
21941      * which property holds the element that used for  hide() / show() / disable() / enable()
21942      * default is 'el' 
21943      */
21944     actionMode : "el",
21945
21946     /** @private */
21947     getActionEl : function(){
21948         return this[this.actionMode];
21949     },
21950
21951     initComponent : Roo.emptyFn,
21952     /**
21953      * If this is a lazy rendering component, render it to its container element.
21954      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21955      */
21956     render : function(container, position){
21957         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21958             if(!container && this.el){
21959                 this.el = Roo.get(this.el);
21960                 container = this.el.dom.parentNode;
21961                 this.allowDomMove = false;
21962             }
21963             this.container = Roo.get(container);
21964             this.rendered = true;
21965             if(position !== undefined){
21966                 if(typeof position == 'number'){
21967                     position = this.container.dom.childNodes[position];
21968                 }else{
21969                     position = Roo.getDom(position);
21970                 }
21971             }
21972             this.onRender(this.container, position || null);
21973             if(this.cls){
21974                 this.el.addClass(this.cls);
21975                 delete this.cls;
21976             }
21977             if(this.style){
21978                 this.el.applyStyles(this.style);
21979                 delete this.style;
21980             }
21981             this.fireEvent("render", this);
21982             this.afterRender(this.container);
21983             if(this.hidden){
21984                 this.hide();
21985             }
21986             if(this.disabled){
21987                 this.disable();
21988             }
21989         }
21990         return this;
21991     },
21992
21993     /** @private */
21994     // default function is not really useful
21995     onRender : function(ct, position){
21996         if(this.el){
21997             this.el = Roo.get(this.el);
21998             if(this.allowDomMove !== false){
21999                 ct.dom.insertBefore(this.el.dom, position);
22000             }
22001         }
22002     },
22003
22004     /** @private */
22005     getAutoCreate : function(){
22006         var cfg = typeof this.autoCreate == "object" ?
22007                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22008         if(this.id && !cfg.id){
22009             cfg.id = this.id;
22010         }
22011         return cfg;
22012     },
22013
22014     /** @private */
22015     afterRender : Roo.emptyFn,
22016
22017     /**
22018      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22019      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22020      */
22021     destroy : function(){
22022         if(this.fireEvent("beforedestroy", this) !== false){
22023             this.purgeListeners();
22024             this.beforeDestroy();
22025             if(this.rendered){
22026                 this.el.removeAllListeners();
22027                 this.el.remove();
22028                 if(this.actionMode == "container"){
22029                     this.container.remove();
22030                 }
22031             }
22032             this.onDestroy();
22033             Roo.ComponentMgr.unregister(this);
22034             this.fireEvent("destroy", this);
22035         }
22036     },
22037
22038         /** @private */
22039     beforeDestroy : function(){
22040
22041     },
22042
22043         /** @private */
22044         onDestroy : function(){
22045
22046     },
22047
22048     /**
22049      * Returns the underlying {@link Roo.Element}.
22050      * @return {Roo.Element} The element
22051      */
22052     getEl : function(){
22053         return this.el;
22054     },
22055
22056     /**
22057      * Returns the id of this component.
22058      * @return {String}
22059      */
22060     getId : function(){
22061         return this.id;
22062     },
22063
22064     /**
22065      * Try to focus this component.
22066      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22067      * @return {Roo.Component} this
22068      */
22069     focus : function(selectText){
22070         if(this.rendered){
22071             this.el.focus();
22072             if(selectText === true){
22073                 this.el.dom.select();
22074             }
22075         }
22076         return this;
22077     },
22078
22079     /** @private */
22080     blur : function(){
22081         if(this.rendered){
22082             this.el.blur();
22083         }
22084         return this;
22085     },
22086
22087     /**
22088      * Disable this component.
22089      * @return {Roo.Component} this
22090      */
22091     disable : function(){
22092         if(this.rendered){
22093             this.onDisable();
22094         }
22095         this.disabled = true;
22096         this.fireEvent("disable", this);
22097         return this;
22098     },
22099
22100         // private
22101     onDisable : function(){
22102         this.getActionEl().addClass(this.disabledClass);
22103         this.el.dom.disabled = true;
22104     },
22105
22106     /**
22107      * Enable this component.
22108      * @return {Roo.Component} this
22109      */
22110     enable : function(){
22111         if(this.rendered){
22112             this.onEnable();
22113         }
22114         this.disabled = false;
22115         this.fireEvent("enable", this);
22116         return this;
22117     },
22118
22119         // private
22120     onEnable : function(){
22121         this.getActionEl().removeClass(this.disabledClass);
22122         this.el.dom.disabled = false;
22123     },
22124
22125     /**
22126      * Convenience function for setting disabled/enabled by boolean.
22127      * @param {Boolean} disabled
22128      */
22129     setDisabled : function(disabled){
22130         this[disabled ? "disable" : "enable"]();
22131     },
22132
22133     /**
22134      * Show this component.
22135      * @return {Roo.Component} this
22136      */
22137     show: function(){
22138         if(this.fireEvent("beforeshow", this) !== false){
22139             this.hidden = false;
22140             if(this.rendered){
22141                 this.onShow();
22142             }
22143             this.fireEvent("show", this);
22144         }
22145         return this;
22146     },
22147
22148     // private
22149     onShow : function(){
22150         var ae = this.getActionEl();
22151         if(this.hideMode == 'visibility'){
22152             ae.dom.style.visibility = "visible";
22153         }else if(this.hideMode == 'offsets'){
22154             ae.removeClass('x-hidden');
22155         }else{
22156             ae.dom.style.display = "";
22157         }
22158     },
22159
22160     /**
22161      * Hide this component.
22162      * @return {Roo.Component} this
22163      */
22164     hide: function(){
22165         if(this.fireEvent("beforehide", this) !== false){
22166             this.hidden = true;
22167             if(this.rendered){
22168                 this.onHide();
22169             }
22170             this.fireEvent("hide", this);
22171         }
22172         return this;
22173     },
22174
22175     // private
22176     onHide : function(){
22177         var ae = this.getActionEl();
22178         if(this.hideMode == 'visibility'){
22179             ae.dom.style.visibility = "hidden";
22180         }else if(this.hideMode == 'offsets'){
22181             ae.addClass('x-hidden');
22182         }else{
22183             ae.dom.style.display = "none";
22184         }
22185     },
22186
22187     /**
22188      * Convenience function to hide or show this component by boolean.
22189      * @param {Boolean} visible True to show, false to hide
22190      * @return {Roo.Component} this
22191      */
22192     setVisible: function(visible){
22193         if(visible) {
22194             this.show();
22195         }else{
22196             this.hide();
22197         }
22198         return this;
22199     },
22200
22201     /**
22202      * Returns true if this component is visible.
22203      */
22204     isVisible : function(){
22205         return this.getActionEl().isVisible();
22206     },
22207
22208     cloneConfig : function(overrides){
22209         overrides = overrides || {};
22210         var id = overrides.id || Roo.id();
22211         var cfg = Roo.applyIf(overrides, this.initialConfig);
22212         cfg.id = id; // prevent dup id
22213         return new this.constructor(cfg);
22214     }
22215 });/*
22216  * Based on:
22217  * Ext JS Library 1.1.1
22218  * Copyright(c) 2006-2007, Ext JS, LLC.
22219  *
22220  * Originally Released Under LGPL - original licence link has changed is not relivant.
22221  *
22222  * Fork - LGPL
22223  * <script type="text/javascript">
22224  */
22225  (function(){ 
22226 /**
22227  * @class Roo.Layer
22228  * @extends Roo.Element
22229  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22230  * automatic maintaining of shadow/shim positions.
22231  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22232  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22233  * you can pass a string with a CSS class name. False turns off the shadow.
22234  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22235  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22236  * @cfg {String} cls CSS class to add to the element
22237  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22238  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22239  * @constructor
22240  * @param {Object} config An object with config options.
22241  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22242  */
22243
22244 Roo.Layer = function(config, existingEl){
22245     config = config || {};
22246     var dh = Roo.DomHelper;
22247     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22248     if(existingEl){
22249         this.dom = Roo.getDom(existingEl);
22250     }
22251     if(!this.dom){
22252         var o = config.dh || {tag: "div", cls: "x-layer"};
22253         this.dom = dh.append(pel, o);
22254     }
22255     if(config.cls){
22256         this.addClass(config.cls);
22257     }
22258     this.constrain = config.constrain !== false;
22259     this.visibilityMode = Roo.Element.VISIBILITY;
22260     if(config.id){
22261         this.id = this.dom.id = config.id;
22262     }else{
22263         this.id = Roo.id(this.dom);
22264     }
22265     this.zindex = config.zindex || this.getZIndex();
22266     this.position("absolute", this.zindex);
22267     if(config.shadow){
22268         this.shadowOffset = config.shadowOffset || 4;
22269         this.shadow = new Roo.Shadow({
22270             offset : this.shadowOffset,
22271             mode : config.shadow
22272         });
22273     }else{
22274         this.shadowOffset = 0;
22275     }
22276     this.useShim = config.shim !== false && Roo.useShims;
22277     this.useDisplay = config.useDisplay;
22278     this.hide();
22279 };
22280
22281 var supr = Roo.Element.prototype;
22282
22283 // shims are shared among layer to keep from having 100 iframes
22284 var shims = [];
22285
22286 Roo.extend(Roo.Layer, Roo.Element, {
22287
22288     getZIndex : function(){
22289         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22290     },
22291
22292     getShim : function(){
22293         if(!this.useShim){
22294             return null;
22295         }
22296         if(this.shim){
22297             return this.shim;
22298         }
22299         var shim = shims.shift();
22300         if(!shim){
22301             shim = this.createShim();
22302             shim.enableDisplayMode('block');
22303             shim.dom.style.display = 'none';
22304             shim.dom.style.visibility = 'visible';
22305         }
22306         var pn = this.dom.parentNode;
22307         if(shim.dom.parentNode != pn){
22308             pn.insertBefore(shim.dom, this.dom);
22309         }
22310         shim.setStyle('z-index', this.getZIndex()-2);
22311         this.shim = shim;
22312         return shim;
22313     },
22314
22315     hideShim : function(){
22316         if(this.shim){
22317             this.shim.setDisplayed(false);
22318             shims.push(this.shim);
22319             delete this.shim;
22320         }
22321     },
22322
22323     disableShadow : function(){
22324         if(this.shadow){
22325             this.shadowDisabled = true;
22326             this.shadow.hide();
22327             this.lastShadowOffset = this.shadowOffset;
22328             this.shadowOffset = 0;
22329         }
22330     },
22331
22332     enableShadow : function(show){
22333         if(this.shadow){
22334             this.shadowDisabled = false;
22335             this.shadowOffset = this.lastShadowOffset;
22336             delete this.lastShadowOffset;
22337             if(show){
22338                 this.sync(true);
22339             }
22340         }
22341     },
22342
22343     // private
22344     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22345     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22346     sync : function(doShow){
22347         var sw = this.shadow;
22348         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22349             var sh = this.getShim();
22350
22351             var w = this.getWidth(),
22352                 h = this.getHeight();
22353
22354             var l = this.getLeft(true),
22355                 t = this.getTop(true);
22356
22357             if(sw && !this.shadowDisabled){
22358                 if(doShow && !sw.isVisible()){
22359                     sw.show(this);
22360                 }else{
22361                     sw.realign(l, t, w, h);
22362                 }
22363                 if(sh){
22364                     if(doShow){
22365                        sh.show();
22366                     }
22367                     // fit the shim behind the shadow, so it is shimmed too
22368                     var a = sw.adjusts, s = sh.dom.style;
22369                     s.left = (Math.min(l, l+a.l))+"px";
22370                     s.top = (Math.min(t, t+a.t))+"px";
22371                     s.width = (w+a.w)+"px";
22372                     s.height = (h+a.h)+"px";
22373                 }
22374             }else if(sh){
22375                 if(doShow){
22376                    sh.show();
22377                 }
22378                 sh.setSize(w, h);
22379                 sh.setLeftTop(l, t);
22380             }
22381             
22382         }
22383     },
22384
22385     // private
22386     destroy : function(){
22387         this.hideShim();
22388         if(this.shadow){
22389             this.shadow.hide();
22390         }
22391         this.removeAllListeners();
22392         var pn = this.dom.parentNode;
22393         if(pn){
22394             pn.removeChild(this.dom);
22395         }
22396         Roo.Element.uncache(this.id);
22397     },
22398
22399     remove : function(){
22400         this.destroy();
22401     },
22402
22403     // private
22404     beginUpdate : function(){
22405         this.updating = true;
22406     },
22407
22408     // private
22409     endUpdate : function(){
22410         this.updating = false;
22411         this.sync(true);
22412     },
22413
22414     // private
22415     hideUnders : function(negOffset){
22416         if(this.shadow){
22417             this.shadow.hide();
22418         }
22419         this.hideShim();
22420     },
22421
22422     // private
22423     constrainXY : function(){
22424         if(this.constrain){
22425             var vw = Roo.lib.Dom.getViewWidth(),
22426                 vh = Roo.lib.Dom.getViewHeight();
22427             var s = Roo.get(document).getScroll();
22428
22429             var xy = this.getXY();
22430             var x = xy[0], y = xy[1];   
22431             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22432             // only move it if it needs it
22433             var moved = false;
22434             // first validate right/bottom
22435             if((x + w) > vw+s.left){
22436                 x = vw - w - this.shadowOffset;
22437                 moved = true;
22438             }
22439             if((y + h) > vh+s.top){
22440                 y = vh - h - this.shadowOffset;
22441                 moved = true;
22442             }
22443             // then make sure top/left isn't negative
22444             if(x < s.left){
22445                 x = s.left;
22446                 moved = true;
22447             }
22448             if(y < s.top){
22449                 y = s.top;
22450                 moved = true;
22451             }
22452             if(moved){
22453                 if(this.avoidY){
22454                     var ay = this.avoidY;
22455                     if(y <= ay && (y+h) >= ay){
22456                         y = ay-h-5;   
22457                     }
22458                 }
22459                 xy = [x, y];
22460                 this.storeXY(xy);
22461                 supr.setXY.call(this, xy);
22462                 this.sync();
22463             }
22464         }
22465     },
22466
22467     isVisible : function(){
22468         return this.visible;    
22469     },
22470
22471     // private
22472     showAction : function(){
22473         this.visible = true; // track visibility to prevent getStyle calls
22474         if(this.useDisplay === true){
22475             this.setDisplayed("");
22476         }else if(this.lastXY){
22477             supr.setXY.call(this, this.lastXY);
22478         }else if(this.lastLT){
22479             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22480         }
22481     },
22482
22483     // private
22484     hideAction : function(){
22485         this.visible = false;
22486         if(this.useDisplay === true){
22487             this.setDisplayed(false);
22488         }else{
22489             this.setLeftTop(-10000,-10000);
22490         }
22491     },
22492
22493     // overridden Element method
22494     setVisible : function(v, a, d, c, e){
22495         if(v){
22496             this.showAction();
22497         }
22498         if(a && v){
22499             var cb = function(){
22500                 this.sync(true);
22501                 if(c){
22502                     c();
22503                 }
22504             }.createDelegate(this);
22505             supr.setVisible.call(this, true, true, d, cb, e);
22506         }else{
22507             if(!v){
22508                 this.hideUnders(true);
22509             }
22510             var cb = c;
22511             if(a){
22512                 cb = function(){
22513                     this.hideAction();
22514                     if(c){
22515                         c();
22516                     }
22517                 }.createDelegate(this);
22518             }
22519             supr.setVisible.call(this, v, a, d, cb, e);
22520             if(v){
22521                 this.sync(true);
22522             }else if(!a){
22523                 this.hideAction();
22524             }
22525         }
22526     },
22527
22528     storeXY : function(xy){
22529         delete this.lastLT;
22530         this.lastXY = xy;
22531     },
22532
22533     storeLeftTop : function(left, top){
22534         delete this.lastXY;
22535         this.lastLT = [left, top];
22536     },
22537
22538     // private
22539     beforeFx : function(){
22540         this.beforeAction();
22541         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22542     },
22543
22544     // private
22545     afterFx : function(){
22546         Roo.Layer.superclass.afterFx.apply(this, arguments);
22547         this.sync(this.isVisible());
22548     },
22549
22550     // private
22551     beforeAction : function(){
22552         if(!this.updating && this.shadow){
22553             this.shadow.hide();
22554         }
22555     },
22556
22557     // overridden Element method
22558     setLeft : function(left){
22559         this.storeLeftTop(left, this.getTop(true));
22560         supr.setLeft.apply(this, arguments);
22561         this.sync();
22562     },
22563
22564     setTop : function(top){
22565         this.storeLeftTop(this.getLeft(true), top);
22566         supr.setTop.apply(this, arguments);
22567         this.sync();
22568     },
22569
22570     setLeftTop : function(left, top){
22571         this.storeLeftTop(left, top);
22572         supr.setLeftTop.apply(this, arguments);
22573         this.sync();
22574     },
22575
22576     setXY : function(xy, a, d, c, e){
22577         this.fixDisplay();
22578         this.beforeAction();
22579         this.storeXY(xy);
22580         var cb = this.createCB(c);
22581         supr.setXY.call(this, xy, a, d, cb, e);
22582         if(!a){
22583             cb();
22584         }
22585     },
22586
22587     // private
22588     createCB : function(c){
22589         var el = this;
22590         return function(){
22591             el.constrainXY();
22592             el.sync(true);
22593             if(c){
22594                 c();
22595             }
22596         };
22597     },
22598
22599     // overridden Element method
22600     setX : function(x, a, d, c, e){
22601         this.setXY([x, this.getY()], a, d, c, e);
22602     },
22603
22604     // overridden Element method
22605     setY : function(y, a, d, c, e){
22606         this.setXY([this.getX(), y], a, d, c, e);
22607     },
22608
22609     // overridden Element method
22610     setSize : function(w, h, a, d, c, e){
22611         this.beforeAction();
22612         var cb = this.createCB(c);
22613         supr.setSize.call(this, w, h, a, d, cb, e);
22614         if(!a){
22615             cb();
22616         }
22617     },
22618
22619     // overridden Element method
22620     setWidth : function(w, a, d, c, e){
22621         this.beforeAction();
22622         var cb = this.createCB(c);
22623         supr.setWidth.call(this, w, a, d, cb, e);
22624         if(!a){
22625             cb();
22626         }
22627     },
22628
22629     // overridden Element method
22630     setHeight : function(h, a, d, c, e){
22631         this.beforeAction();
22632         var cb = this.createCB(c);
22633         supr.setHeight.call(this, h, a, d, cb, e);
22634         if(!a){
22635             cb();
22636         }
22637     },
22638
22639     // overridden Element method
22640     setBounds : function(x, y, w, h, a, d, c, e){
22641         this.beforeAction();
22642         var cb = this.createCB(c);
22643         if(!a){
22644             this.storeXY([x, y]);
22645             supr.setXY.call(this, [x, y]);
22646             supr.setSize.call(this, w, h, a, d, cb, e);
22647             cb();
22648         }else{
22649             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22650         }
22651         return this;
22652     },
22653     
22654     /**
22655      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22656      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22657      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22658      * @param {Number} zindex The new z-index to set
22659      * @return {this} The Layer
22660      */
22661     setZIndex : function(zindex){
22662         this.zindex = zindex;
22663         this.setStyle("z-index", zindex + 2);
22664         if(this.shadow){
22665             this.shadow.setZIndex(zindex + 1);
22666         }
22667         if(this.shim){
22668             this.shim.setStyle("z-index", zindex);
22669         }
22670     }
22671 });
22672 })();/*
22673  * Based on:
22674  * Ext JS Library 1.1.1
22675  * Copyright(c) 2006-2007, Ext JS, LLC.
22676  *
22677  * Originally Released Under LGPL - original licence link has changed is not relivant.
22678  *
22679  * Fork - LGPL
22680  * <script type="text/javascript">
22681  */
22682
22683
22684 /**
22685  * @class Roo.Shadow
22686  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22687  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22688  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22689  * @constructor
22690  * Create a new Shadow
22691  * @param {Object} config The config object
22692  */
22693 Roo.Shadow = function(config){
22694     Roo.apply(this, config);
22695     if(typeof this.mode != "string"){
22696         this.mode = this.defaultMode;
22697     }
22698     var o = this.offset, a = {h: 0};
22699     var rad = Math.floor(this.offset/2);
22700     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22701         case "drop":
22702             a.w = 0;
22703             a.l = a.t = o;
22704             a.t -= 1;
22705             if(Roo.isIE){
22706                 a.l -= this.offset + rad;
22707                 a.t -= this.offset + rad;
22708                 a.w -= rad;
22709                 a.h -= rad;
22710                 a.t += 1;
22711             }
22712         break;
22713         case "sides":
22714             a.w = (o*2);
22715             a.l = -o;
22716             a.t = o-1;
22717             if(Roo.isIE){
22718                 a.l -= (this.offset - rad);
22719                 a.t -= this.offset + rad;
22720                 a.l += 1;
22721                 a.w -= (this.offset - rad)*2;
22722                 a.w -= rad + 1;
22723                 a.h -= 1;
22724             }
22725         break;
22726         case "frame":
22727             a.w = a.h = (o*2);
22728             a.l = a.t = -o;
22729             a.t += 1;
22730             a.h -= 2;
22731             if(Roo.isIE){
22732                 a.l -= (this.offset - rad);
22733                 a.t -= (this.offset - rad);
22734                 a.l += 1;
22735                 a.w -= (this.offset + rad + 1);
22736                 a.h -= (this.offset + rad);
22737                 a.h += 1;
22738             }
22739         break;
22740     };
22741
22742     this.adjusts = a;
22743 };
22744
22745 Roo.Shadow.prototype = {
22746     /**
22747      * @cfg {String} mode
22748      * The shadow display mode.  Supports the following options:<br />
22749      * sides: Shadow displays on both sides and bottom only<br />
22750      * frame: Shadow displays equally on all four sides<br />
22751      * drop: Traditional bottom-right drop shadow (default)
22752      */
22753     /**
22754      * @cfg {String} offset
22755      * The number of pixels to offset the shadow from the element (defaults to 4)
22756      */
22757     offset: 4,
22758
22759     // private
22760     defaultMode: "drop",
22761
22762     /**
22763      * Displays the shadow under the target element
22764      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22765      */
22766     show : function(target){
22767         target = Roo.get(target);
22768         if(!this.el){
22769             this.el = Roo.Shadow.Pool.pull();
22770             if(this.el.dom.nextSibling != target.dom){
22771                 this.el.insertBefore(target);
22772             }
22773         }
22774         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22775         if(Roo.isIE){
22776             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22777         }
22778         this.realign(
22779             target.getLeft(true),
22780             target.getTop(true),
22781             target.getWidth(),
22782             target.getHeight()
22783         );
22784         this.el.dom.style.display = "block";
22785     },
22786
22787     /**
22788      * Returns true if the shadow is visible, else false
22789      */
22790     isVisible : function(){
22791         return this.el ? true : false;  
22792     },
22793
22794     /**
22795      * Direct alignment when values are already available. Show must be called at least once before
22796      * calling this method to ensure it is initialized.
22797      * @param {Number} left The target element left position
22798      * @param {Number} top The target element top position
22799      * @param {Number} width The target element width
22800      * @param {Number} height The target element height
22801      */
22802     realign : function(l, t, w, h){
22803         if(!this.el){
22804             return;
22805         }
22806         var a = this.adjusts, d = this.el.dom, s = d.style;
22807         var iea = 0;
22808         s.left = (l+a.l)+"px";
22809         s.top = (t+a.t)+"px";
22810         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22811  
22812         if(s.width != sws || s.height != shs){
22813             s.width = sws;
22814             s.height = shs;
22815             if(!Roo.isIE){
22816                 var cn = d.childNodes;
22817                 var sww = Math.max(0, (sw-12))+"px";
22818                 cn[0].childNodes[1].style.width = sww;
22819                 cn[1].childNodes[1].style.width = sww;
22820                 cn[2].childNodes[1].style.width = sww;
22821                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22822             }
22823         }
22824     },
22825
22826     /**
22827      * Hides this shadow
22828      */
22829     hide : function(){
22830         if(this.el){
22831             this.el.dom.style.display = "none";
22832             Roo.Shadow.Pool.push(this.el);
22833             delete this.el;
22834         }
22835     },
22836
22837     /**
22838      * Adjust the z-index of this shadow
22839      * @param {Number} zindex The new z-index
22840      */
22841     setZIndex : function(z){
22842         this.zIndex = z;
22843         if(this.el){
22844             this.el.setStyle("z-index", z);
22845         }
22846     }
22847 };
22848
22849 // Private utility class that manages the internal Shadow cache
22850 Roo.Shadow.Pool = function(){
22851     var p = [];
22852     var markup = Roo.isIE ?
22853                  '<div class="x-ie-shadow"></div>' :
22854                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22855     return {
22856         pull : function(){
22857             var sh = p.shift();
22858             if(!sh){
22859                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22860                 sh.autoBoxAdjust = false;
22861             }
22862             return sh;
22863         },
22864
22865         push : function(sh){
22866             p.push(sh);
22867         }
22868     };
22869 }();/*
22870  * Based on:
22871  * Ext JS Library 1.1.1
22872  * Copyright(c) 2006-2007, Ext JS, LLC.
22873  *
22874  * Originally Released Under LGPL - original licence link has changed is not relivant.
22875  *
22876  * Fork - LGPL
22877  * <script type="text/javascript">
22878  */
22879
22880 /**
22881  * @class Roo.BoxComponent
22882  * @extends Roo.Component
22883  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22884  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22885  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22886  * layout containers.
22887  * @constructor
22888  * @param {Roo.Element/String/Object} config The configuration options.
22889  */
22890 Roo.BoxComponent = function(config){
22891     Roo.Component.call(this, config);
22892     this.addEvents({
22893         /**
22894          * @event resize
22895          * Fires after the component is resized.
22896              * @param {Roo.Component} this
22897              * @param {Number} adjWidth The box-adjusted width that was set
22898              * @param {Number} adjHeight The box-adjusted height that was set
22899              * @param {Number} rawWidth The width that was originally specified
22900              * @param {Number} rawHeight The height that was originally specified
22901              */
22902         resize : true,
22903         /**
22904          * @event move
22905          * Fires after the component is moved.
22906              * @param {Roo.Component} this
22907              * @param {Number} x The new x position
22908              * @param {Number} y The new y position
22909              */
22910         move : true
22911     });
22912 };
22913
22914 Roo.extend(Roo.BoxComponent, Roo.Component, {
22915     // private, set in afterRender to signify that the component has been rendered
22916     boxReady : false,
22917     // private, used to defer height settings to subclasses
22918     deferHeight: false,
22919     /** @cfg {Number} width
22920      * width (optional) size of component
22921      */
22922      /** @cfg {Number} height
22923      * height (optional) size of component
22924      */
22925      
22926     /**
22927      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22928      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22929      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22930      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22931      * @return {Roo.BoxComponent} this
22932      */
22933     setSize : function(w, h){
22934         // support for standard size objects
22935         if(typeof w == 'object'){
22936             h = w.height;
22937             w = w.width;
22938         }
22939         // not rendered
22940         if(!this.boxReady){
22941             this.width = w;
22942             this.height = h;
22943             return this;
22944         }
22945
22946         // prevent recalcs when not needed
22947         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22948             return this;
22949         }
22950         this.lastSize = {width: w, height: h};
22951
22952         var adj = this.adjustSize(w, h);
22953         var aw = adj.width, ah = adj.height;
22954         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22955             var rz = this.getResizeEl();
22956             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22957                 rz.setSize(aw, ah);
22958             }else if(!this.deferHeight && ah !== undefined){
22959                 rz.setHeight(ah);
22960             }else if(aw !== undefined){
22961                 rz.setWidth(aw);
22962             }
22963             this.onResize(aw, ah, w, h);
22964             this.fireEvent('resize', this, aw, ah, w, h);
22965         }
22966         return this;
22967     },
22968
22969     /**
22970      * Gets the current size of the component's underlying element.
22971      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22972      */
22973     getSize : function(){
22974         return this.el.getSize();
22975     },
22976
22977     /**
22978      * Gets the current XY position of the component's underlying element.
22979      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22980      * @return {Array} The XY position of the element (e.g., [100, 200])
22981      */
22982     getPosition : function(local){
22983         if(local === true){
22984             return [this.el.getLeft(true), this.el.getTop(true)];
22985         }
22986         return this.xy || this.el.getXY();
22987     },
22988
22989     /**
22990      * Gets the current box measurements of the component's underlying element.
22991      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22992      * @returns {Object} box An object in the format {x, y, width, height}
22993      */
22994     getBox : function(local){
22995         var s = this.el.getSize();
22996         if(local){
22997             s.x = this.el.getLeft(true);
22998             s.y = this.el.getTop(true);
22999         }else{
23000             var xy = this.xy || this.el.getXY();
23001             s.x = xy[0];
23002             s.y = xy[1];
23003         }
23004         return s;
23005     },
23006
23007     /**
23008      * Sets the current box measurements of the component's underlying element.
23009      * @param {Object} box An object in the format {x, y, width, height}
23010      * @returns {Roo.BoxComponent} this
23011      */
23012     updateBox : function(box){
23013         this.setSize(box.width, box.height);
23014         this.setPagePosition(box.x, box.y);
23015         return this;
23016     },
23017
23018     // protected
23019     getResizeEl : function(){
23020         return this.resizeEl || this.el;
23021     },
23022
23023     // protected
23024     getPositionEl : function(){
23025         return this.positionEl || this.el;
23026     },
23027
23028     /**
23029      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23030      * This method fires the move event.
23031      * @param {Number} left The new left
23032      * @param {Number} top The new top
23033      * @returns {Roo.BoxComponent} this
23034      */
23035     setPosition : function(x, y){
23036         this.x = x;
23037         this.y = y;
23038         if(!this.boxReady){
23039             return this;
23040         }
23041         var adj = this.adjustPosition(x, y);
23042         var ax = adj.x, ay = adj.y;
23043
23044         var el = this.getPositionEl();
23045         if(ax !== undefined || ay !== undefined){
23046             if(ax !== undefined && ay !== undefined){
23047                 el.setLeftTop(ax, ay);
23048             }else if(ax !== undefined){
23049                 el.setLeft(ax);
23050             }else if(ay !== undefined){
23051                 el.setTop(ay);
23052             }
23053             this.onPosition(ax, ay);
23054             this.fireEvent('move', this, ax, ay);
23055         }
23056         return this;
23057     },
23058
23059     /**
23060      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23061      * This method fires the move event.
23062      * @param {Number} x The new x position
23063      * @param {Number} y The new y position
23064      * @returns {Roo.BoxComponent} this
23065      */
23066     setPagePosition : function(x, y){
23067         this.pageX = x;
23068         this.pageY = y;
23069         if(!this.boxReady){
23070             return;
23071         }
23072         if(x === undefined || y === undefined){ // cannot translate undefined points
23073             return;
23074         }
23075         var p = this.el.translatePoints(x, y);
23076         this.setPosition(p.left, p.top);
23077         return this;
23078     },
23079
23080     // private
23081     onRender : function(ct, position){
23082         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23083         if(this.resizeEl){
23084             this.resizeEl = Roo.get(this.resizeEl);
23085         }
23086         if(this.positionEl){
23087             this.positionEl = Roo.get(this.positionEl);
23088         }
23089     },
23090
23091     // private
23092     afterRender : function(){
23093         Roo.BoxComponent.superclass.afterRender.call(this);
23094         this.boxReady = true;
23095         this.setSize(this.width, this.height);
23096         if(this.x || this.y){
23097             this.setPosition(this.x, this.y);
23098         }
23099         if(this.pageX || this.pageY){
23100             this.setPagePosition(this.pageX, this.pageY);
23101         }
23102     },
23103
23104     /**
23105      * Force the component's size to recalculate based on the underlying element's current height and width.
23106      * @returns {Roo.BoxComponent} this
23107      */
23108     syncSize : function(){
23109         delete this.lastSize;
23110         this.setSize(this.el.getWidth(), this.el.getHeight());
23111         return this;
23112     },
23113
23114     /**
23115      * Called after the component is resized, this method is empty by default but can be implemented by any
23116      * subclass that needs to perform custom logic after a resize occurs.
23117      * @param {Number} adjWidth The box-adjusted width that was set
23118      * @param {Number} adjHeight The box-adjusted height that was set
23119      * @param {Number} rawWidth The width that was originally specified
23120      * @param {Number} rawHeight The height that was originally specified
23121      */
23122     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23123
23124     },
23125
23126     /**
23127      * Called after the component is moved, this method is empty by default but can be implemented by any
23128      * subclass that needs to perform custom logic after a move occurs.
23129      * @param {Number} x The new x position
23130      * @param {Number} y The new y position
23131      */
23132     onPosition : function(x, y){
23133
23134     },
23135
23136     // private
23137     adjustSize : function(w, h){
23138         if(this.autoWidth){
23139             w = 'auto';
23140         }
23141         if(this.autoHeight){
23142             h = 'auto';
23143         }
23144         return {width : w, height: h};
23145     },
23146
23147     // private
23148     adjustPosition : function(x, y){
23149         return {x : x, y: y};
23150     }
23151 });/*
23152  * Based on:
23153  * Ext JS Library 1.1.1
23154  * Copyright(c) 2006-2007, Ext JS, LLC.
23155  *
23156  * Originally Released Under LGPL - original licence link has changed is not relivant.
23157  *
23158  * Fork - LGPL
23159  * <script type="text/javascript">
23160  */
23161
23162
23163 /**
23164  * @class Roo.SplitBar
23165  * @extends Roo.util.Observable
23166  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23167  * <br><br>
23168  * Usage:
23169  * <pre><code>
23170 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23171                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23172 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23173 split.minSize = 100;
23174 split.maxSize = 600;
23175 split.animate = true;
23176 split.on('moved', splitterMoved);
23177 </code></pre>
23178  * @constructor
23179  * Create a new SplitBar
23180  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23181  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23182  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23183  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23184                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23185                         position of the SplitBar).
23186  */
23187 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23188     
23189     /** @private */
23190     this.el = Roo.get(dragElement, true);
23191     this.el.dom.unselectable = "on";
23192     /** @private */
23193     this.resizingEl = Roo.get(resizingElement, true);
23194
23195     /**
23196      * @private
23197      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23198      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23199      * @type Number
23200      */
23201     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23202     
23203     /**
23204      * The minimum size of the resizing element. (Defaults to 0)
23205      * @type Number
23206      */
23207     this.minSize = 0;
23208     
23209     /**
23210      * The maximum size of the resizing element. (Defaults to 2000)
23211      * @type Number
23212      */
23213     this.maxSize = 2000;
23214     
23215     /**
23216      * Whether to animate the transition to the new size
23217      * @type Boolean
23218      */
23219     this.animate = false;
23220     
23221     /**
23222      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23223      * @type Boolean
23224      */
23225     this.useShim = false;
23226     
23227     /** @private */
23228     this.shim = null;
23229     
23230     if(!existingProxy){
23231         /** @private */
23232         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23233     }else{
23234         this.proxy = Roo.get(existingProxy).dom;
23235     }
23236     /** @private */
23237     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23238     
23239     /** @private */
23240     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23241     
23242     /** @private */
23243     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23244     
23245     /** @private */
23246     this.dragSpecs = {};
23247     
23248     /**
23249      * @private The adapter to use to positon and resize elements
23250      */
23251     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23252     this.adapter.init(this);
23253     
23254     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23255         /** @private */
23256         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23257         this.el.addClass("x-splitbar-h");
23258     }else{
23259         /** @private */
23260         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23261         this.el.addClass("x-splitbar-v");
23262     }
23263     
23264     this.addEvents({
23265         /**
23266          * @event resize
23267          * Fires when the splitter is moved (alias for {@link #event-moved})
23268          * @param {Roo.SplitBar} this
23269          * @param {Number} newSize the new width or height
23270          */
23271         "resize" : true,
23272         /**
23273          * @event moved
23274          * Fires when the splitter is moved
23275          * @param {Roo.SplitBar} this
23276          * @param {Number} newSize the new width or height
23277          */
23278         "moved" : true,
23279         /**
23280          * @event beforeresize
23281          * Fires before the splitter is dragged
23282          * @param {Roo.SplitBar} this
23283          */
23284         "beforeresize" : true,
23285
23286         "beforeapply" : true
23287     });
23288
23289     Roo.util.Observable.call(this);
23290 };
23291
23292 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23293     onStartProxyDrag : function(x, y){
23294         this.fireEvent("beforeresize", this);
23295         if(!this.overlay){
23296             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23297             o.unselectable();
23298             o.enableDisplayMode("block");
23299             // all splitbars share the same overlay
23300             Roo.SplitBar.prototype.overlay = o;
23301         }
23302         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23303         this.overlay.show();
23304         Roo.get(this.proxy).setDisplayed("block");
23305         var size = this.adapter.getElementSize(this);
23306         this.activeMinSize = this.getMinimumSize();;
23307         this.activeMaxSize = this.getMaximumSize();;
23308         var c1 = size - this.activeMinSize;
23309         var c2 = Math.max(this.activeMaxSize - size, 0);
23310         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23311             this.dd.resetConstraints();
23312             this.dd.setXConstraint(
23313                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23314                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23315             );
23316             this.dd.setYConstraint(0, 0);
23317         }else{
23318             this.dd.resetConstraints();
23319             this.dd.setXConstraint(0, 0);
23320             this.dd.setYConstraint(
23321                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23322                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23323             );
23324          }
23325         this.dragSpecs.startSize = size;
23326         this.dragSpecs.startPoint = [x, y];
23327         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23328     },
23329     
23330     /** 
23331      * @private Called after the drag operation by the DDProxy
23332      */
23333     onEndProxyDrag : function(e){
23334         Roo.get(this.proxy).setDisplayed(false);
23335         var endPoint = Roo.lib.Event.getXY(e);
23336         if(this.overlay){
23337             this.overlay.hide();
23338         }
23339         var newSize;
23340         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23341             newSize = this.dragSpecs.startSize + 
23342                 (this.placement == Roo.SplitBar.LEFT ?
23343                     endPoint[0] - this.dragSpecs.startPoint[0] :
23344                     this.dragSpecs.startPoint[0] - endPoint[0]
23345                 );
23346         }else{
23347             newSize = this.dragSpecs.startSize + 
23348                 (this.placement == Roo.SplitBar.TOP ?
23349                     endPoint[1] - this.dragSpecs.startPoint[1] :
23350                     this.dragSpecs.startPoint[1] - endPoint[1]
23351                 );
23352         }
23353         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23354         if(newSize != this.dragSpecs.startSize){
23355             if(this.fireEvent('beforeapply', this, newSize) !== false){
23356                 this.adapter.setElementSize(this, newSize);
23357                 this.fireEvent("moved", this, newSize);
23358                 this.fireEvent("resize", this, newSize);
23359             }
23360         }
23361     },
23362     
23363     /**
23364      * Get the adapter this SplitBar uses
23365      * @return The adapter object
23366      */
23367     getAdapter : function(){
23368         return this.adapter;
23369     },
23370     
23371     /**
23372      * Set the adapter this SplitBar uses
23373      * @param {Object} adapter A SplitBar adapter object
23374      */
23375     setAdapter : function(adapter){
23376         this.adapter = adapter;
23377         this.adapter.init(this);
23378     },
23379     
23380     /**
23381      * Gets the minimum size for the resizing element
23382      * @return {Number} The minimum size
23383      */
23384     getMinimumSize : function(){
23385         return this.minSize;
23386     },
23387     
23388     /**
23389      * Sets the minimum size for the resizing element
23390      * @param {Number} minSize The minimum size
23391      */
23392     setMinimumSize : function(minSize){
23393         this.minSize = minSize;
23394     },
23395     
23396     /**
23397      * Gets the maximum size for the resizing element
23398      * @return {Number} The maximum size
23399      */
23400     getMaximumSize : function(){
23401         return this.maxSize;
23402     },
23403     
23404     /**
23405      * Sets the maximum size for the resizing element
23406      * @param {Number} maxSize The maximum size
23407      */
23408     setMaximumSize : function(maxSize){
23409         this.maxSize = maxSize;
23410     },
23411     
23412     /**
23413      * Sets the initialize size for the resizing element
23414      * @param {Number} size The initial size
23415      */
23416     setCurrentSize : function(size){
23417         var oldAnimate = this.animate;
23418         this.animate = false;
23419         this.adapter.setElementSize(this, size);
23420         this.animate = oldAnimate;
23421     },
23422     
23423     /**
23424      * Destroy this splitbar. 
23425      * @param {Boolean} removeEl True to remove the element
23426      */
23427     destroy : function(removeEl){
23428         if(this.shim){
23429             this.shim.remove();
23430         }
23431         this.dd.unreg();
23432         this.proxy.parentNode.removeChild(this.proxy);
23433         if(removeEl){
23434             this.el.remove();
23435         }
23436     }
23437 });
23438
23439 /**
23440  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23441  */
23442 Roo.SplitBar.createProxy = function(dir){
23443     var proxy = new Roo.Element(document.createElement("div"));
23444     proxy.unselectable();
23445     var cls = 'x-splitbar-proxy';
23446     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23447     document.body.appendChild(proxy.dom);
23448     return proxy.dom;
23449 };
23450
23451 /** 
23452  * @class Roo.SplitBar.BasicLayoutAdapter
23453  * Default Adapter. It assumes the splitter and resizing element are not positioned
23454  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23455  */
23456 Roo.SplitBar.BasicLayoutAdapter = function(){
23457 };
23458
23459 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23460     // do nothing for now
23461     init : function(s){
23462     
23463     },
23464     /**
23465      * Called before drag operations to get the current size of the resizing element. 
23466      * @param {Roo.SplitBar} s The SplitBar using this adapter
23467      */
23468      getElementSize : function(s){
23469         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23470             return s.resizingEl.getWidth();
23471         }else{
23472             return s.resizingEl.getHeight();
23473         }
23474     },
23475     
23476     /**
23477      * Called after drag operations to set the size of the resizing element.
23478      * @param {Roo.SplitBar} s The SplitBar using this adapter
23479      * @param {Number} newSize The new size to set
23480      * @param {Function} onComplete A function to be invoked when resizing is complete
23481      */
23482     setElementSize : function(s, newSize, onComplete){
23483         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23484             if(!s.animate){
23485                 s.resizingEl.setWidth(newSize);
23486                 if(onComplete){
23487                     onComplete(s, newSize);
23488                 }
23489             }else{
23490                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23491             }
23492         }else{
23493             
23494             if(!s.animate){
23495                 s.resizingEl.setHeight(newSize);
23496                 if(onComplete){
23497                     onComplete(s, newSize);
23498                 }
23499             }else{
23500                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23501             }
23502         }
23503     }
23504 };
23505
23506 /** 
23507  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23508  * @extends Roo.SplitBar.BasicLayoutAdapter
23509  * Adapter that  moves the splitter element to align with the resized sizing element. 
23510  * Used with an absolute positioned SplitBar.
23511  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23512  * document.body, make sure you assign an id to the body element.
23513  */
23514 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23515     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23516     this.container = Roo.get(container);
23517 };
23518
23519 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23520     init : function(s){
23521         this.basic.init(s);
23522     },
23523     
23524     getElementSize : function(s){
23525         return this.basic.getElementSize(s);
23526     },
23527     
23528     setElementSize : function(s, newSize, onComplete){
23529         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23530     },
23531     
23532     moveSplitter : function(s){
23533         var yes = Roo.SplitBar;
23534         switch(s.placement){
23535             case yes.LEFT:
23536                 s.el.setX(s.resizingEl.getRight());
23537                 break;
23538             case yes.RIGHT:
23539                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23540                 break;
23541             case yes.TOP:
23542                 s.el.setY(s.resizingEl.getBottom());
23543                 break;
23544             case yes.BOTTOM:
23545                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23546                 break;
23547         }
23548     }
23549 };
23550
23551 /**
23552  * Orientation constant - Create a vertical SplitBar
23553  * @static
23554  * @type Number
23555  */
23556 Roo.SplitBar.VERTICAL = 1;
23557
23558 /**
23559  * Orientation constant - Create a horizontal SplitBar
23560  * @static
23561  * @type Number
23562  */
23563 Roo.SplitBar.HORIZONTAL = 2;
23564
23565 /**
23566  * Placement constant - The resizing element is to the left of the splitter element
23567  * @static
23568  * @type Number
23569  */
23570 Roo.SplitBar.LEFT = 1;
23571
23572 /**
23573  * Placement constant - The resizing element is to the right of the splitter element
23574  * @static
23575  * @type Number
23576  */
23577 Roo.SplitBar.RIGHT = 2;
23578
23579 /**
23580  * Placement constant - The resizing element is positioned above the splitter element
23581  * @static
23582  * @type Number
23583  */
23584 Roo.SplitBar.TOP = 3;
23585
23586 /**
23587  * Placement constant - The resizing element is positioned under splitter element
23588  * @static
23589  * @type Number
23590  */
23591 Roo.SplitBar.BOTTOM = 4;
23592 /*
23593  * Based on:
23594  * Ext JS Library 1.1.1
23595  * Copyright(c) 2006-2007, Ext JS, LLC.
23596  *
23597  * Originally Released Under LGPL - original licence link has changed is not relivant.
23598  *
23599  * Fork - LGPL
23600  * <script type="text/javascript">
23601  */
23602
23603 /**
23604  * @class Roo.View
23605  * @extends Roo.util.Observable
23606  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23607  * This class also supports single and multi selection modes. <br>
23608  * Create a data model bound view:
23609  <pre><code>
23610  var store = new Roo.data.Store(...);
23611
23612  var view = new Roo.View({
23613     el : "my-element",
23614     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23615  
23616     singleSelect: true,
23617     selectedClass: "ydataview-selected",
23618     store: store
23619  });
23620
23621  // listen for node click?
23622  view.on("click", function(vw, index, node, e){
23623  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23624  });
23625
23626  // load XML data
23627  dataModel.load("foobar.xml");
23628  </code></pre>
23629  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23630  * <br><br>
23631  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23632  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23633  * 
23634  * Note: old style constructor is still suported (container, template, config)
23635  * 
23636  * @constructor
23637  * Create a new View
23638  * @param {Object} config The config object
23639  * 
23640  */
23641 Roo.View = function(config, depreciated_tpl, depreciated_config){
23642     
23643     if (typeof(depreciated_tpl) == 'undefined') {
23644         // new way.. - universal constructor.
23645         Roo.apply(this, config);
23646         this.el  = Roo.get(this.el);
23647     } else {
23648         // old format..
23649         this.el  = Roo.get(config);
23650         this.tpl = depreciated_tpl;
23651         Roo.apply(this, depreciated_config);
23652     }
23653      
23654     
23655     if(typeof(this.tpl) == "string"){
23656         this.tpl = new Roo.Template(this.tpl);
23657     } else {
23658         // support xtype ctors..
23659         this.tpl = new Roo.factory(this.tpl, Roo);
23660     }
23661     
23662     
23663     this.tpl.compile();
23664    
23665
23666      
23667     /** @private */
23668     this.addEvents({
23669         /**
23670          * @event beforeclick
23671          * Fires before a click is processed. Returns false to cancel the default action.
23672          * @param {Roo.View} this
23673          * @param {Number} index The index of the target node
23674          * @param {HTMLElement} node The target node
23675          * @param {Roo.EventObject} e The raw event object
23676          */
23677             "beforeclick" : true,
23678         /**
23679          * @event click
23680          * Fires when a template node is clicked.
23681          * @param {Roo.View} this
23682          * @param {Number} index The index of the target node
23683          * @param {HTMLElement} node The target node
23684          * @param {Roo.EventObject} e The raw event object
23685          */
23686             "click" : true,
23687         /**
23688          * @event dblclick
23689          * Fires when a template node is double clicked.
23690          * @param {Roo.View} this
23691          * @param {Number} index The index of the target node
23692          * @param {HTMLElement} node The target node
23693          * @param {Roo.EventObject} e The raw event object
23694          */
23695             "dblclick" : true,
23696         /**
23697          * @event contextmenu
23698          * Fires when a template node is right clicked.
23699          * @param {Roo.View} this
23700          * @param {Number} index The index of the target node
23701          * @param {HTMLElement} node The target node
23702          * @param {Roo.EventObject} e The raw event object
23703          */
23704             "contextmenu" : true,
23705         /**
23706          * @event selectionchange
23707          * Fires when the selected nodes change.
23708          * @param {Roo.View} this
23709          * @param {Array} selections Array of the selected nodes
23710          */
23711             "selectionchange" : true,
23712     
23713         /**
23714          * @event beforeselect
23715          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23716          * @param {Roo.View} this
23717          * @param {HTMLElement} node The node to be selected
23718          * @param {Array} selections Array of currently selected nodes
23719          */
23720             "beforeselect" : true,
23721         /**
23722          * @event preparedata
23723          * Fires on every row to render, to allow you to change the data.
23724          * @param {Roo.View} this
23725          * @param {Object} data to be rendered (change this)
23726          */
23727           "preparedata" : true
23728         });
23729
23730     this.el.on({
23731         "click": this.onClick,
23732         "dblclick": this.onDblClick,
23733         "contextmenu": this.onContextMenu,
23734         scope:this
23735     });
23736
23737     this.selections = [];
23738     this.nodes = [];
23739     this.cmp = new Roo.CompositeElementLite([]);
23740     if(this.store){
23741         this.store = Roo.factory(this.store, Roo.data);
23742         this.setStore(this.store, true);
23743     }
23744     Roo.View.superclass.constructor.call(this);
23745 };
23746
23747 Roo.extend(Roo.View, Roo.util.Observable, {
23748     
23749      /**
23750      * @cfg {Roo.data.Store} store Data store to load data from.
23751      */
23752     store : false,
23753     
23754     /**
23755      * @cfg {String|Roo.Element} el The container element.
23756      */
23757     el : '',
23758     
23759     /**
23760      * @cfg {String|Roo.Template} tpl The template used by this View 
23761      */
23762     tpl : false,
23763     /**
23764      * @cfg {String} dataName the named area of the template to use as the data area
23765      *                          Works with domtemplates roo-name="name"
23766      */
23767     dataName: false,
23768     /**
23769      * @cfg {String} selectedClass The css class to add to selected nodes
23770      */
23771     selectedClass : "x-view-selected",
23772      /**
23773      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23774      */
23775     emptyText : "",
23776     /**
23777      * @cfg {Boolean} multiSelect Allow multiple selection
23778      */
23779     multiSelect : false,
23780     /**
23781      * @cfg {Boolean} singleSelect Allow single selection
23782      */
23783     singleSelect:  false,
23784     
23785     /**
23786      * @cfg {Boolean} toggleSelect - selecting 
23787      */
23788     toggleSelect : false,
23789     
23790     /**
23791      * Returns the element this view is bound to.
23792      * @return {Roo.Element}
23793      */
23794     getEl : function(){
23795         return this.el;
23796     },
23797
23798     /**
23799      * Refreshes the view.
23800      */
23801     refresh : function(){
23802         var t = this.tpl;
23803         
23804         // if we are using something like 'domtemplate', then
23805         // the what gets used is:
23806         // t.applySubtemplate(NAME, data, wrapping data..)
23807         // the outer template then get' applied with
23808         //     the store 'extra data'
23809         // and the body get's added to the
23810         //      roo-name="data" node?
23811         //      <span class='roo-tpl-{name}'></span> ?????
23812         
23813         
23814         
23815         this.clearSelections();
23816         this.el.update("");
23817         var html = [];
23818         var records = this.store.getRange();
23819         if(records.length < 1) {
23820             
23821             // is this valid??  = should it render a template??
23822             
23823             this.el.update(this.emptyText);
23824             return;
23825         }
23826         var el = this.el;
23827         if (this.dataName) {
23828             this.el.update(t.apply(this.store.meta)); //????
23829             el = this.el.child('.roo-tpl-' + this.dataName);
23830         }
23831         
23832         for(var i = 0, len = records.length; i < len; i++){
23833             var data = this.prepareData(records[i].data, i, records[i]);
23834             this.fireEvent("preparedata", this, data, i, records[i]);
23835             html[html.length] = Roo.util.Format.trim(
23836                 this.dataName ?
23837                     t.applySubtemplate(this.dataName, data, this.store.meta) :
23838                     t.apply(data)
23839             );
23840         }
23841         
23842         
23843         
23844         el.update(html.join(""));
23845         this.nodes = el.dom.childNodes;
23846         this.updateIndexes(0);
23847     },
23848
23849     /**
23850      * Function to override to reformat the data that is sent to
23851      * the template for each node.
23852      * DEPRICATED - use the preparedata event handler.
23853      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23854      * a JSON object for an UpdateManager bound view).
23855      */
23856     prepareData : function(data, index, record)
23857     {
23858         this.fireEvent("preparedata", this, data, index, record);
23859         return data;
23860     },
23861
23862     onUpdate : function(ds, record){
23863         this.clearSelections();
23864         var index = this.store.indexOf(record);
23865         var n = this.nodes[index];
23866         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
23867         n.parentNode.removeChild(n);
23868         this.updateIndexes(index, index);
23869     },
23870
23871     
23872     
23873 // --------- FIXME     
23874     onAdd : function(ds, records, index)
23875     {
23876         this.clearSelections();
23877         if(this.nodes.length == 0){
23878             this.refresh();
23879             return;
23880         }
23881         var n = this.nodes[index];
23882         for(var i = 0, len = records.length; i < len; i++){
23883             var d = this.prepareData(records[i].data, i, records[i]);
23884             if(n){
23885                 this.tpl.insertBefore(n, d);
23886             }else{
23887                 
23888                 this.tpl.append(this.el, d);
23889             }
23890         }
23891         this.updateIndexes(index);
23892     },
23893
23894     onRemove : function(ds, record, index){
23895         this.clearSelections();
23896         var el = this.dataName  ?
23897             this.el.child('.roo-tpl-' + this.dataName) :
23898             this.el; 
23899         el.dom.removeChild(this.nodes[index]);
23900         this.updateIndexes(index);
23901     },
23902
23903     /**
23904      * Refresh an individual node.
23905      * @param {Number} index
23906      */
23907     refreshNode : function(index){
23908         this.onUpdate(this.store, this.store.getAt(index));
23909     },
23910
23911     updateIndexes : function(startIndex, endIndex){
23912         var ns = this.nodes;
23913         startIndex = startIndex || 0;
23914         endIndex = endIndex || ns.length - 1;
23915         for(var i = startIndex; i <= endIndex; i++){
23916             ns[i].nodeIndex = i;
23917         }
23918     },
23919
23920     /**
23921      * Changes the data store this view uses and refresh the view.
23922      * @param {Store} store
23923      */
23924     setStore : function(store, initial){
23925         if(!initial && this.store){
23926             this.store.un("datachanged", this.refresh);
23927             this.store.un("add", this.onAdd);
23928             this.store.un("remove", this.onRemove);
23929             this.store.un("update", this.onUpdate);
23930             this.store.un("clear", this.refresh);
23931         }
23932         if(store){
23933           
23934             store.on("datachanged", this.refresh, this);
23935             store.on("add", this.onAdd, this);
23936             store.on("remove", this.onRemove, this);
23937             store.on("update", this.onUpdate, this);
23938             store.on("clear", this.refresh, this);
23939         }
23940         
23941         if(store){
23942             this.refresh();
23943         }
23944     },
23945
23946     /**
23947      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23948      * @param {HTMLElement} node
23949      * @return {HTMLElement} The template node
23950      */
23951     findItemFromChild : function(node){
23952         var el = this.dataName  ?
23953             this.el.child('.roo-tpl-' + this.dataName,true) :
23954             this.el.dom; 
23955         
23956         if(!node || node.parentNode == el){
23957                     return node;
23958             }
23959             var p = node.parentNode;
23960             while(p && p != el){
23961             if(p.parentNode == el){
23962                 return p;
23963             }
23964             p = p.parentNode;
23965         }
23966             return null;
23967     },
23968
23969     /** @ignore */
23970     onClick : function(e){
23971         var item = this.findItemFromChild(e.getTarget());
23972         if(item){
23973             var index = this.indexOf(item);
23974             if(this.onItemClick(item, index, e) !== false){
23975                 this.fireEvent("click", this, index, item, e);
23976             }
23977         }else{
23978             this.clearSelections();
23979         }
23980     },
23981
23982     /** @ignore */
23983     onContextMenu : function(e){
23984         var item = this.findItemFromChild(e.getTarget());
23985         if(item){
23986             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23987         }
23988     },
23989
23990     /** @ignore */
23991     onDblClick : function(e){
23992         var item = this.findItemFromChild(e.getTarget());
23993         if(item){
23994             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23995         }
23996     },
23997
23998     onItemClick : function(item, index, e)
23999     {
24000         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24001             return false;
24002         }
24003         if (this.toggleSelect) {
24004             var m = this.isSelected(item) ? 'unselect' : 'select';
24005             Roo.log(m);
24006             var _t = this;
24007             _t[m](item, true, false);
24008             return true;
24009         }
24010         if(this.multiSelect || this.singleSelect){
24011             if(this.multiSelect && e.shiftKey && this.lastSelection){
24012                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24013             }else{
24014                 this.select(item, this.multiSelect && e.ctrlKey);
24015                 this.lastSelection = item;
24016             }
24017             e.preventDefault();
24018         }
24019         return true;
24020     },
24021
24022     /**
24023      * Get the number of selected nodes.
24024      * @return {Number}
24025      */
24026     getSelectionCount : function(){
24027         return this.selections.length;
24028     },
24029
24030     /**
24031      * Get the currently selected nodes.
24032      * @return {Array} An array of HTMLElements
24033      */
24034     getSelectedNodes : function(){
24035         return this.selections;
24036     },
24037
24038     /**
24039      * Get the indexes of the selected nodes.
24040      * @return {Array}
24041      */
24042     getSelectedIndexes : function(){
24043         var indexes = [], s = this.selections;
24044         for(var i = 0, len = s.length; i < len; i++){
24045             indexes.push(s[i].nodeIndex);
24046         }
24047         return indexes;
24048     },
24049
24050     /**
24051      * Clear all selections
24052      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24053      */
24054     clearSelections : function(suppressEvent){
24055         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24056             this.cmp.elements = this.selections;
24057             this.cmp.removeClass(this.selectedClass);
24058             this.selections = [];
24059             if(!suppressEvent){
24060                 this.fireEvent("selectionchange", this, this.selections);
24061             }
24062         }
24063     },
24064
24065     /**
24066      * Returns true if the passed node is selected
24067      * @param {HTMLElement/Number} node The node or node index
24068      * @return {Boolean}
24069      */
24070     isSelected : function(node){
24071         var s = this.selections;
24072         if(s.length < 1){
24073             return false;
24074         }
24075         node = this.getNode(node);
24076         return s.indexOf(node) !== -1;
24077     },
24078
24079     /**
24080      * Selects nodes.
24081      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24082      * @param {Boolean} keepExisting (optional) true to keep existing selections
24083      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24084      */
24085     select : function(nodeInfo, keepExisting, suppressEvent){
24086         if(nodeInfo instanceof Array){
24087             if(!keepExisting){
24088                 this.clearSelections(true);
24089             }
24090             for(var i = 0, len = nodeInfo.length; i < len; i++){
24091                 this.select(nodeInfo[i], true, true);
24092             }
24093             return;
24094         } 
24095         var node = this.getNode(nodeInfo);
24096         if(!node || this.isSelected(node)){
24097             return; // already selected.
24098         }
24099         if(!keepExisting){
24100             this.clearSelections(true);
24101         }
24102         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24103             Roo.fly(node).addClass(this.selectedClass);
24104             this.selections.push(node);
24105             if(!suppressEvent){
24106                 this.fireEvent("selectionchange", this, this.selections);
24107             }
24108         }
24109         
24110         
24111     },
24112       /**
24113      * Unselects nodes.
24114      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24115      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24116      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24117      */
24118     unselect : function(nodeInfo, keepExisting, suppressEvent)
24119     {
24120         if(nodeInfo instanceof Array){
24121             Roo.each(this.selections, function(s) {
24122                 this.unselect(s, nodeInfo);
24123             }, this);
24124             return;
24125         }
24126         var node = this.getNode(nodeInfo);
24127         if(!node || !this.isSelected(node)){
24128             Roo.log("not selected");
24129             return; // not selected.
24130         }
24131         // fireevent???
24132         var ns = [];
24133         Roo.each(this.selections, function(s) {
24134             if (s == node ) {
24135                 Roo.fly(node).removeClass(this.selectedClass);
24136
24137                 return;
24138             }
24139             ns.push(s);
24140         },this);
24141         
24142         this.selections= ns;
24143         this.fireEvent("selectionchange", this, this.selections);
24144     },
24145
24146     /**
24147      * Gets a template node.
24148      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24149      * @return {HTMLElement} The node or null if it wasn't found
24150      */
24151     getNode : function(nodeInfo){
24152         if(typeof nodeInfo == "string"){
24153             return document.getElementById(nodeInfo);
24154         }else if(typeof nodeInfo == "number"){
24155             return this.nodes[nodeInfo];
24156         }
24157         return nodeInfo;
24158     },
24159
24160     /**
24161      * Gets a range template nodes.
24162      * @param {Number} startIndex
24163      * @param {Number} endIndex
24164      * @return {Array} An array of nodes
24165      */
24166     getNodes : function(start, end){
24167         var ns = this.nodes;
24168         start = start || 0;
24169         end = typeof end == "undefined" ? ns.length - 1 : end;
24170         var nodes = [];
24171         if(start <= end){
24172             for(var i = start; i <= end; i++){
24173                 nodes.push(ns[i]);
24174             }
24175         } else{
24176             for(var i = start; i >= end; i--){
24177                 nodes.push(ns[i]);
24178             }
24179         }
24180         return nodes;
24181     },
24182
24183     /**
24184      * Finds the index of the passed node
24185      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24186      * @return {Number} The index of the node or -1
24187      */
24188     indexOf : function(node){
24189         node = this.getNode(node);
24190         if(typeof node.nodeIndex == "number"){
24191             return node.nodeIndex;
24192         }
24193         var ns = this.nodes;
24194         for(var i = 0, len = ns.length; i < len; i++){
24195             if(ns[i] == node){
24196                 return i;
24197             }
24198         }
24199         return -1;
24200     }
24201 });
24202 /*
24203  * Based on:
24204  * Ext JS Library 1.1.1
24205  * Copyright(c) 2006-2007, Ext JS, LLC.
24206  *
24207  * Originally Released Under LGPL - original licence link has changed is not relivant.
24208  *
24209  * Fork - LGPL
24210  * <script type="text/javascript">
24211  */
24212
24213 /**
24214  * @class Roo.JsonView
24215  * @extends Roo.View
24216  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24217 <pre><code>
24218 var view = new Roo.JsonView({
24219     container: "my-element",
24220     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24221     multiSelect: true, 
24222     jsonRoot: "data" 
24223 });
24224
24225 // listen for node click?
24226 view.on("click", function(vw, index, node, e){
24227     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24228 });
24229
24230 // direct load of JSON data
24231 view.load("foobar.php");
24232
24233 // Example from my blog list
24234 var tpl = new Roo.Template(
24235     '&lt;div class="entry"&gt;' +
24236     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24237     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24238     "&lt;/div&gt;&lt;hr /&gt;"
24239 );
24240
24241 var moreView = new Roo.JsonView({
24242     container :  "entry-list", 
24243     template : tpl,
24244     jsonRoot: "posts"
24245 });
24246 moreView.on("beforerender", this.sortEntries, this);
24247 moreView.load({
24248     url: "/blog/get-posts.php",
24249     params: "allposts=true",
24250     text: "Loading Blog Entries..."
24251 });
24252 </code></pre>
24253
24254 * Note: old code is supported with arguments : (container, template, config)
24255
24256
24257  * @constructor
24258  * Create a new JsonView
24259  * 
24260  * @param {Object} config The config object
24261  * 
24262  */
24263 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24264     
24265     
24266     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24267
24268     var um = this.el.getUpdateManager();
24269     um.setRenderer(this);
24270     um.on("update", this.onLoad, this);
24271     um.on("failure", this.onLoadException, this);
24272
24273     /**
24274      * @event beforerender
24275      * Fires before rendering of the downloaded JSON data.
24276      * @param {Roo.JsonView} this
24277      * @param {Object} data The JSON data loaded
24278      */
24279     /**
24280      * @event load
24281      * Fires when data is loaded.
24282      * @param {Roo.JsonView} this
24283      * @param {Object} data The JSON data loaded
24284      * @param {Object} response The raw Connect response object
24285      */
24286     /**
24287      * @event loadexception
24288      * Fires when loading fails.
24289      * @param {Roo.JsonView} this
24290      * @param {Object} response The raw Connect response object
24291      */
24292     this.addEvents({
24293         'beforerender' : true,
24294         'load' : true,
24295         'loadexception' : true
24296     });
24297 };
24298 Roo.extend(Roo.JsonView, Roo.View, {
24299     /**
24300      * @type {String} The root property in the loaded JSON object that contains the data
24301      */
24302     jsonRoot : "",
24303
24304     /**
24305      * Refreshes the view.
24306      */
24307     refresh : function(){
24308         this.clearSelections();
24309         this.el.update("");
24310         var html = [];
24311         var o = this.jsonData;
24312         if(o && o.length > 0){
24313             for(var i = 0, len = o.length; i < len; i++){
24314                 var data = this.prepareData(o[i], i, o);
24315                 html[html.length] = this.tpl.apply(data);
24316             }
24317         }else{
24318             html.push(this.emptyText);
24319         }
24320         this.el.update(html.join(""));
24321         this.nodes = this.el.dom.childNodes;
24322         this.updateIndexes(0);
24323     },
24324
24325     /**
24326      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
24327      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
24328      <pre><code>
24329      view.load({
24330          url: "your-url.php",
24331          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24332          callback: yourFunction,
24333          scope: yourObject, //(optional scope)
24334          discardUrl: false,
24335          nocache: false,
24336          text: "Loading...",
24337          timeout: 30,
24338          scripts: false
24339      });
24340      </code></pre>
24341      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24342      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
24343      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
24344      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24345      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
24346      */
24347     load : function(){
24348         var um = this.el.getUpdateManager();
24349         um.update.apply(um, arguments);
24350     },
24351
24352     render : function(el, response){
24353         this.clearSelections();
24354         this.el.update("");
24355         var o;
24356         try{
24357             o = Roo.util.JSON.decode(response.responseText);
24358             if(this.jsonRoot){
24359                 
24360                 o = o[this.jsonRoot];
24361             }
24362         } catch(e){
24363         }
24364         /**
24365          * The current JSON data or null
24366          */
24367         this.jsonData = o;
24368         this.beforeRender();
24369         this.refresh();
24370     },
24371
24372 /**
24373  * Get the number of records in the current JSON dataset
24374  * @return {Number}
24375  */
24376     getCount : function(){
24377         return this.jsonData ? this.jsonData.length : 0;
24378     },
24379
24380 /**
24381  * Returns the JSON object for the specified node(s)
24382  * @param {HTMLElement/Array} node The node or an array of nodes
24383  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24384  * you get the JSON object for the node
24385  */
24386     getNodeData : function(node){
24387         if(node instanceof Array){
24388             var data = [];
24389             for(var i = 0, len = node.length; i < len; i++){
24390                 data.push(this.getNodeData(node[i]));
24391             }
24392             return data;
24393         }
24394         return this.jsonData[this.indexOf(node)] || null;
24395     },
24396
24397     beforeRender : function(){
24398         this.snapshot = this.jsonData;
24399         if(this.sortInfo){
24400             this.sort.apply(this, this.sortInfo);
24401         }
24402         this.fireEvent("beforerender", this, this.jsonData);
24403     },
24404
24405     onLoad : function(el, o){
24406         this.fireEvent("load", this, this.jsonData, o);
24407     },
24408
24409     onLoadException : function(el, o){
24410         this.fireEvent("loadexception", this, o);
24411     },
24412
24413 /**
24414  * Filter the data by a specific property.
24415  * @param {String} property A property on your JSON objects
24416  * @param {String/RegExp} value Either string that the property values
24417  * should start with, or a RegExp to test against the property
24418  */
24419     filter : function(property, value){
24420         if(this.jsonData){
24421             var data = [];
24422             var ss = this.snapshot;
24423             if(typeof value == "string"){
24424                 var vlen = value.length;
24425                 if(vlen == 0){
24426                     this.clearFilter();
24427                     return;
24428                 }
24429                 value = value.toLowerCase();
24430                 for(var i = 0, len = ss.length; i < len; i++){
24431                     var o = ss[i];
24432                     if(o[property].substr(0, vlen).toLowerCase() == value){
24433                         data.push(o);
24434                     }
24435                 }
24436             } else if(value.exec){ // regex?
24437                 for(var i = 0, len = ss.length; i < len; i++){
24438                     var o = ss[i];
24439                     if(value.test(o[property])){
24440                         data.push(o);
24441                     }
24442                 }
24443             } else{
24444                 return;
24445             }
24446             this.jsonData = data;
24447             this.refresh();
24448         }
24449     },
24450
24451 /**
24452  * Filter by a function. The passed function will be called with each
24453  * object in the current dataset. If the function returns true the value is kept,
24454  * otherwise it is filtered.
24455  * @param {Function} fn
24456  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24457  */
24458     filterBy : function(fn, scope){
24459         if(this.jsonData){
24460             var data = [];
24461             var ss = this.snapshot;
24462             for(var i = 0, len = ss.length; i < len; i++){
24463                 var o = ss[i];
24464                 if(fn.call(scope || this, o)){
24465                     data.push(o);
24466                 }
24467             }
24468             this.jsonData = data;
24469             this.refresh();
24470         }
24471     },
24472
24473 /**
24474  * Clears the current filter.
24475  */
24476     clearFilter : function(){
24477         if(this.snapshot && this.jsonData != this.snapshot){
24478             this.jsonData = this.snapshot;
24479             this.refresh();
24480         }
24481     },
24482
24483
24484 /**
24485  * Sorts the data for this view and refreshes it.
24486  * @param {String} property A property on your JSON objects to sort on
24487  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24488  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24489  */
24490     sort : function(property, dir, sortType){
24491         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24492         if(this.jsonData){
24493             var p = property;
24494             var dsc = dir && dir.toLowerCase() == "desc";
24495             var f = function(o1, o2){
24496                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24497                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24498                 ;
24499                 if(v1 < v2){
24500                     return dsc ? +1 : -1;
24501                 } else if(v1 > v2){
24502                     return dsc ? -1 : +1;
24503                 } else{
24504                     return 0;
24505                 }
24506             };
24507             this.jsonData.sort(f);
24508             this.refresh();
24509             if(this.jsonData != this.snapshot){
24510                 this.snapshot.sort(f);
24511             }
24512         }
24513     }
24514 });/*
24515  * Based on:
24516  * Ext JS Library 1.1.1
24517  * Copyright(c) 2006-2007, Ext JS, LLC.
24518  *
24519  * Originally Released Under LGPL - original licence link has changed is not relivant.
24520  *
24521  * Fork - LGPL
24522  * <script type="text/javascript">
24523  */
24524  
24525
24526 /**
24527  * @class Roo.ColorPalette
24528  * @extends Roo.Component
24529  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24530  * Here's an example of typical usage:
24531  * <pre><code>
24532 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24533 cp.render('my-div');
24534
24535 cp.on('select', function(palette, selColor){
24536     // do something with selColor
24537 });
24538 </code></pre>
24539  * @constructor
24540  * Create a new ColorPalette
24541  * @param {Object} config The config object
24542  */
24543 Roo.ColorPalette = function(config){
24544     Roo.ColorPalette.superclass.constructor.call(this, config);
24545     this.addEvents({
24546         /**
24547              * @event select
24548              * Fires when a color is selected
24549              * @param {ColorPalette} this
24550              * @param {String} color The 6-digit color hex code (without the # symbol)
24551              */
24552         select: true
24553     });
24554
24555     if(this.handler){
24556         this.on("select", this.handler, this.scope, true);
24557     }
24558 };
24559 Roo.extend(Roo.ColorPalette, Roo.Component, {
24560     /**
24561      * @cfg {String} itemCls
24562      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24563      */
24564     itemCls : "x-color-palette",
24565     /**
24566      * @cfg {String} value
24567      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24568      * the hex codes are case-sensitive.
24569      */
24570     value : null,
24571     clickEvent:'click',
24572     // private
24573     ctype: "Roo.ColorPalette",
24574
24575     /**
24576      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24577      */
24578     allowReselect : false,
24579
24580     /**
24581      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24582      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24583      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24584      * of colors with the width setting until the box is symmetrical.</p>
24585      * <p>You can override individual colors if needed:</p>
24586      * <pre><code>
24587 var cp = new Roo.ColorPalette();
24588 cp.colors[0] = "FF0000";  // change the first box to red
24589 </code></pre>
24590
24591 Or you can provide a custom array of your own for complete control:
24592 <pre><code>
24593 var cp = new Roo.ColorPalette();
24594 cp.colors = ["000000", "993300", "333300"];
24595 </code></pre>
24596      * @type Array
24597      */
24598     colors : [
24599         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24600         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24601         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24602         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24603         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24604     ],
24605
24606     // private
24607     onRender : function(container, position){
24608         var t = new Roo.MasterTemplate(
24609             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24610         );
24611         var c = this.colors;
24612         for(var i = 0, len = c.length; i < len; i++){
24613             t.add([c[i]]);
24614         }
24615         var el = document.createElement("div");
24616         el.className = this.itemCls;
24617         t.overwrite(el);
24618         container.dom.insertBefore(el, position);
24619         this.el = Roo.get(el);
24620         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24621         if(this.clickEvent != 'click'){
24622             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24623         }
24624     },
24625
24626     // private
24627     afterRender : function(){
24628         Roo.ColorPalette.superclass.afterRender.call(this);
24629         if(this.value){
24630             var s = this.value;
24631             this.value = null;
24632             this.select(s);
24633         }
24634     },
24635
24636     // private
24637     handleClick : function(e, t){
24638         e.preventDefault();
24639         if(!this.disabled){
24640             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24641             this.select(c.toUpperCase());
24642         }
24643     },
24644
24645     /**
24646      * Selects the specified color in the palette (fires the select event)
24647      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24648      */
24649     select : function(color){
24650         color = color.replace("#", "");
24651         if(color != this.value || this.allowReselect){
24652             var el = this.el;
24653             if(this.value){
24654                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24655             }
24656             el.child("a.color-"+color).addClass("x-color-palette-sel");
24657             this.value = color;
24658             this.fireEvent("select", this, color);
24659         }
24660     }
24661 });/*
24662  * Based on:
24663  * Ext JS Library 1.1.1
24664  * Copyright(c) 2006-2007, Ext JS, LLC.
24665  *
24666  * Originally Released Under LGPL - original licence link has changed is not relivant.
24667  *
24668  * Fork - LGPL
24669  * <script type="text/javascript">
24670  */
24671  
24672 /**
24673  * @class Roo.DatePicker
24674  * @extends Roo.Component
24675  * Simple date picker class.
24676  * @constructor
24677  * Create a new DatePicker
24678  * @param {Object} config The config object
24679  */
24680 Roo.DatePicker = function(config){
24681     Roo.DatePicker.superclass.constructor.call(this, config);
24682
24683     this.value = config && config.value ?
24684                  config.value.clearTime() : new Date().clearTime();
24685
24686     this.addEvents({
24687         /**
24688              * @event select
24689              * Fires when a date is selected
24690              * @param {DatePicker} this
24691              * @param {Date} date The selected date
24692              */
24693         'select': true,
24694         /**
24695              * @event monthchange
24696              * Fires when the displayed month changes 
24697              * @param {DatePicker} this
24698              * @param {Date} date The selected month
24699              */
24700         'monthchange': true
24701     });
24702
24703     if(this.handler){
24704         this.on("select", this.handler,  this.scope || this);
24705     }
24706     // build the disabledDatesRE
24707     if(!this.disabledDatesRE && this.disabledDates){
24708         var dd = this.disabledDates;
24709         var re = "(?:";
24710         for(var i = 0; i < dd.length; i++){
24711             re += dd[i];
24712             if(i != dd.length-1) re += "|";
24713         }
24714         this.disabledDatesRE = new RegExp(re + ")");
24715     }
24716 };
24717
24718 Roo.extend(Roo.DatePicker, Roo.Component, {
24719     /**
24720      * @cfg {String} todayText
24721      * The text to display on the button that selects the current date (defaults to "Today")
24722      */
24723     todayText : "Today",
24724     /**
24725      * @cfg {String} okText
24726      * The text to display on the ok button
24727      */
24728     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24729     /**
24730      * @cfg {String} cancelText
24731      * The text to display on the cancel button
24732      */
24733     cancelText : "Cancel",
24734     /**
24735      * @cfg {String} todayTip
24736      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24737      */
24738     todayTip : "{0} (Spacebar)",
24739     /**
24740      * @cfg {Date} minDate
24741      * Minimum allowable date (JavaScript date object, defaults to null)
24742      */
24743     minDate : null,
24744     /**
24745      * @cfg {Date} maxDate
24746      * Maximum allowable date (JavaScript date object, defaults to null)
24747      */
24748     maxDate : null,
24749     /**
24750      * @cfg {String} minText
24751      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24752      */
24753     minText : "This date is before the minimum date",
24754     /**
24755      * @cfg {String} maxText
24756      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24757      */
24758     maxText : "This date is after the maximum date",
24759     /**
24760      * @cfg {String} format
24761      * The default date format string which can be overriden for localization support.  The format must be
24762      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24763      */
24764     format : "m/d/y",
24765     /**
24766      * @cfg {Array} disabledDays
24767      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24768      */
24769     disabledDays : null,
24770     /**
24771      * @cfg {String} disabledDaysText
24772      * The tooltip to display when the date falls on a disabled day (defaults to "")
24773      */
24774     disabledDaysText : "",
24775     /**
24776      * @cfg {RegExp} disabledDatesRE
24777      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24778      */
24779     disabledDatesRE : null,
24780     /**
24781      * @cfg {String} disabledDatesText
24782      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24783      */
24784     disabledDatesText : "",
24785     /**
24786      * @cfg {Boolean} constrainToViewport
24787      * True to constrain the date picker to the viewport (defaults to true)
24788      */
24789     constrainToViewport : true,
24790     /**
24791      * @cfg {Array} monthNames
24792      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24793      */
24794     monthNames : Date.monthNames,
24795     /**
24796      * @cfg {Array} dayNames
24797      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24798      */
24799     dayNames : Date.dayNames,
24800     /**
24801      * @cfg {String} nextText
24802      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24803      */
24804     nextText: 'Next Month (Control+Right)',
24805     /**
24806      * @cfg {String} prevText
24807      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24808      */
24809     prevText: 'Previous Month (Control+Left)',
24810     /**
24811      * @cfg {String} monthYearText
24812      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24813      */
24814     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24815     /**
24816      * @cfg {Number} startDay
24817      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24818      */
24819     startDay : 0,
24820     /**
24821      * @cfg {Bool} showClear
24822      * Show a clear button (usefull for date form elements that can be blank.)
24823      */
24824     
24825     showClear: false,
24826     
24827     /**
24828      * Sets the value of the date field
24829      * @param {Date} value The date to set
24830      */
24831     setValue : function(value){
24832         var old = this.value;
24833         this.value = value.clearTime(true);
24834         if(this.el){
24835             this.update(this.value);
24836         }
24837     },
24838
24839     /**
24840      * Gets the current selected value of the date field
24841      * @return {Date} The selected date
24842      */
24843     getValue : function(){
24844         return this.value;
24845     },
24846
24847     // private
24848     focus : function(){
24849         if(this.el){
24850             this.update(this.activeDate);
24851         }
24852     },
24853
24854     // private
24855     onRender : function(container, position){
24856         var m = [
24857              '<table cellspacing="0">',
24858                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24859                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24860         var dn = this.dayNames;
24861         for(var i = 0; i < 7; i++){
24862             var d = this.startDay+i;
24863             if(d > 6){
24864                 d = d-7;
24865             }
24866             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24867         }
24868         m[m.length] = "</tr></thead><tbody><tr>";
24869         for(var i = 0; i < 42; i++) {
24870             if(i % 7 == 0 && i != 0){
24871                 m[m.length] = "</tr><tr>";
24872             }
24873             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24874         }
24875         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24876             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24877
24878         var el = document.createElement("div");
24879         el.className = "x-date-picker";
24880         el.innerHTML = m.join("");
24881
24882         container.dom.insertBefore(el, position);
24883
24884         this.el = Roo.get(el);
24885         this.eventEl = Roo.get(el.firstChild);
24886
24887         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24888             handler: this.showPrevMonth,
24889             scope: this,
24890             preventDefault:true,
24891             stopDefault:true
24892         });
24893
24894         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24895             handler: this.showNextMonth,
24896             scope: this,
24897             preventDefault:true,
24898             stopDefault:true
24899         });
24900
24901         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24902
24903         this.monthPicker = this.el.down('div.x-date-mp');
24904         this.monthPicker.enableDisplayMode('block');
24905         
24906         var kn = new Roo.KeyNav(this.eventEl, {
24907             "left" : function(e){
24908                 e.ctrlKey ?
24909                     this.showPrevMonth() :
24910                     this.update(this.activeDate.add("d", -1));
24911             },
24912
24913             "right" : function(e){
24914                 e.ctrlKey ?
24915                     this.showNextMonth() :
24916                     this.update(this.activeDate.add("d", 1));
24917             },
24918
24919             "up" : function(e){
24920                 e.ctrlKey ?
24921                     this.showNextYear() :
24922                     this.update(this.activeDate.add("d", -7));
24923             },
24924
24925             "down" : function(e){
24926                 e.ctrlKey ?
24927                     this.showPrevYear() :
24928                     this.update(this.activeDate.add("d", 7));
24929             },
24930
24931             "pageUp" : function(e){
24932                 this.showNextMonth();
24933             },
24934
24935             "pageDown" : function(e){
24936                 this.showPrevMonth();
24937             },
24938
24939             "enter" : function(e){
24940                 e.stopPropagation();
24941                 return true;
24942             },
24943
24944             scope : this
24945         });
24946
24947         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24948
24949         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24950
24951         this.el.unselectable();
24952         
24953         this.cells = this.el.select("table.x-date-inner tbody td");
24954         this.textNodes = this.el.query("table.x-date-inner tbody span");
24955
24956         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24957             text: "&#160;",
24958             tooltip: this.monthYearText
24959         });
24960
24961         this.mbtn.on('click', this.showMonthPicker, this);
24962         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24963
24964
24965         var today = (new Date()).dateFormat(this.format);
24966         
24967         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24968         if (this.showClear) {
24969             baseTb.add( new Roo.Toolbar.Fill());
24970         }
24971         baseTb.add({
24972             text: String.format(this.todayText, today),
24973             tooltip: String.format(this.todayTip, today),
24974             handler: this.selectToday,
24975             scope: this
24976         });
24977         
24978         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24979             
24980         //});
24981         if (this.showClear) {
24982             
24983             baseTb.add( new Roo.Toolbar.Fill());
24984             baseTb.add({
24985                 text: '&#160;',
24986                 cls: 'x-btn-icon x-btn-clear',
24987                 handler: function() {
24988                     //this.value = '';
24989                     this.fireEvent("select", this, '');
24990                 },
24991                 scope: this
24992             });
24993         }
24994         
24995         
24996         if(Roo.isIE){
24997             this.el.repaint();
24998         }
24999         this.update(this.value);
25000     },
25001
25002     createMonthPicker : function(){
25003         if(!this.monthPicker.dom.firstChild){
25004             var buf = ['<table border="0" cellspacing="0">'];
25005             for(var i = 0; i < 6; i++){
25006                 buf.push(
25007                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25008                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25009                     i == 0 ?
25010                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
25011                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25012                 );
25013             }
25014             buf.push(
25015                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25016                     this.okText,
25017                     '</button><button type="button" class="x-date-mp-cancel">',
25018                     this.cancelText,
25019                     '</button></td></tr>',
25020                 '</table>'
25021             );
25022             this.monthPicker.update(buf.join(''));
25023             this.monthPicker.on('click', this.onMonthClick, this);
25024             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25025
25026             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25027             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25028
25029             this.mpMonths.each(function(m, a, i){
25030                 i += 1;
25031                 if((i%2) == 0){
25032                     m.dom.xmonth = 5 + Math.round(i * .5);
25033                 }else{
25034                     m.dom.xmonth = Math.round((i-1) * .5);
25035                 }
25036             });
25037         }
25038     },
25039
25040     showMonthPicker : function(){
25041         this.createMonthPicker();
25042         var size = this.el.getSize();
25043         this.monthPicker.setSize(size);
25044         this.monthPicker.child('table').setSize(size);
25045
25046         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25047         this.updateMPMonth(this.mpSelMonth);
25048         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25049         this.updateMPYear(this.mpSelYear);
25050
25051         this.monthPicker.slideIn('t', {duration:.2});
25052     },
25053
25054     updateMPYear : function(y){
25055         this.mpyear = y;
25056         var ys = this.mpYears.elements;
25057         for(var i = 1; i <= 10; i++){
25058             var td = ys[i-1], y2;
25059             if((i%2) == 0){
25060                 y2 = y + Math.round(i * .5);
25061                 td.firstChild.innerHTML = y2;
25062                 td.xyear = y2;
25063             }else{
25064                 y2 = y - (5-Math.round(i * .5));
25065                 td.firstChild.innerHTML = y2;
25066                 td.xyear = y2;
25067             }
25068             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25069         }
25070     },
25071
25072     updateMPMonth : function(sm){
25073         this.mpMonths.each(function(m, a, i){
25074             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25075         });
25076     },
25077
25078     selectMPMonth: function(m){
25079         
25080     },
25081
25082     onMonthClick : function(e, t){
25083         e.stopEvent();
25084         var el = new Roo.Element(t), pn;
25085         if(el.is('button.x-date-mp-cancel')){
25086             this.hideMonthPicker();
25087         }
25088         else if(el.is('button.x-date-mp-ok')){
25089             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25090             this.hideMonthPicker();
25091         }
25092         else if(pn = el.up('td.x-date-mp-month', 2)){
25093             this.mpMonths.removeClass('x-date-mp-sel');
25094             pn.addClass('x-date-mp-sel');
25095             this.mpSelMonth = pn.dom.xmonth;
25096         }
25097         else if(pn = el.up('td.x-date-mp-year', 2)){
25098             this.mpYears.removeClass('x-date-mp-sel');
25099             pn.addClass('x-date-mp-sel');
25100             this.mpSelYear = pn.dom.xyear;
25101         }
25102         else if(el.is('a.x-date-mp-prev')){
25103             this.updateMPYear(this.mpyear-10);
25104         }
25105         else if(el.is('a.x-date-mp-next')){
25106             this.updateMPYear(this.mpyear+10);
25107         }
25108     },
25109
25110     onMonthDblClick : function(e, t){
25111         e.stopEvent();
25112         var el = new Roo.Element(t), pn;
25113         if(pn = el.up('td.x-date-mp-month', 2)){
25114             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25115             this.hideMonthPicker();
25116         }
25117         else if(pn = el.up('td.x-date-mp-year', 2)){
25118             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25119             this.hideMonthPicker();
25120         }
25121     },
25122
25123     hideMonthPicker : function(disableAnim){
25124         if(this.monthPicker){
25125             if(disableAnim === true){
25126                 this.monthPicker.hide();
25127             }else{
25128                 this.monthPicker.slideOut('t', {duration:.2});
25129             }
25130         }
25131     },
25132
25133     // private
25134     showPrevMonth : function(e){
25135         this.update(this.activeDate.add("mo", -1));
25136     },
25137
25138     // private
25139     showNextMonth : function(e){
25140         this.update(this.activeDate.add("mo", 1));
25141     },
25142
25143     // private
25144     showPrevYear : function(){
25145         this.update(this.activeDate.add("y", -1));
25146     },
25147
25148     // private
25149     showNextYear : function(){
25150         this.update(this.activeDate.add("y", 1));
25151     },
25152
25153     // private
25154     handleMouseWheel : function(e){
25155         var delta = e.getWheelDelta();
25156         if(delta > 0){
25157             this.showPrevMonth();
25158             e.stopEvent();
25159         } else if(delta < 0){
25160             this.showNextMonth();
25161             e.stopEvent();
25162         }
25163     },
25164
25165     // private
25166     handleDateClick : function(e, t){
25167         e.stopEvent();
25168         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25169             this.setValue(new Date(t.dateValue));
25170             this.fireEvent("select", this, this.value);
25171         }
25172     },
25173
25174     // private
25175     selectToday : function(){
25176         this.setValue(new Date().clearTime());
25177         this.fireEvent("select", this, this.value);
25178     },
25179
25180     // private
25181     update : function(date)
25182     {
25183         var vd = this.activeDate;
25184         this.activeDate = date;
25185         if(vd && this.el){
25186             var t = date.getTime();
25187             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25188                 this.cells.removeClass("x-date-selected");
25189                 this.cells.each(function(c){
25190                    if(c.dom.firstChild.dateValue == t){
25191                        c.addClass("x-date-selected");
25192                        setTimeout(function(){
25193                             try{c.dom.firstChild.focus();}catch(e){}
25194                        }, 50);
25195                        return false;
25196                    }
25197                 });
25198                 return;
25199             }
25200         }
25201         
25202         var days = date.getDaysInMonth();
25203         var firstOfMonth = date.getFirstDateOfMonth();
25204         var startingPos = firstOfMonth.getDay()-this.startDay;
25205
25206         if(startingPos <= this.startDay){
25207             startingPos += 7;
25208         }
25209
25210         var pm = date.add("mo", -1);
25211         var prevStart = pm.getDaysInMonth()-startingPos;
25212
25213         var cells = this.cells.elements;
25214         var textEls = this.textNodes;
25215         days += startingPos;
25216
25217         // convert everything to numbers so it's fast
25218         var day = 86400000;
25219         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25220         var today = new Date().clearTime().getTime();
25221         var sel = date.clearTime().getTime();
25222         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25223         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25224         var ddMatch = this.disabledDatesRE;
25225         var ddText = this.disabledDatesText;
25226         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25227         var ddaysText = this.disabledDaysText;
25228         var format = this.format;
25229
25230         var setCellClass = function(cal, cell){
25231             cell.title = "";
25232             var t = d.getTime();
25233             cell.firstChild.dateValue = t;
25234             if(t == today){
25235                 cell.className += " x-date-today";
25236                 cell.title = cal.todayText;
25237             }
25238             if(t == sel){
25239                 cell.className += " x-date-selected";
25240                 setTimeout(function(){
25241                     try{cell.firstChild.focus();}catch(e){}
25242                 }, 50);
25243             }
25244             // disabling
25245             if(t < min) {
25246                 cell.className = " x-date-disabled";
25247                 cell.title = cal.minText;
25248                 return;
25249             }
25250             if(t > max) {
25251                 cell.className = " x-date-disabled";
25252                 cell.title = cal.maxText;
25253                 return;
25254             }
25255             if(ddays){
25256                 if(ddays.indexOf(d.getDay()) != -1){
25257                     cell.title = ddaysText;
25258                     cell.className = " x-date-disabled";
25259                 }
25260             }
25261             if(ddMatch && format){
25262                 var fvalue = d.dateFormat(format);
25263                 if(ddMatch.test(fvalue)){
25264                     cell.title = ddText.replace("%0", fvalue);
25265                     cell.className = " x-date-disabled";
25266                 }
25267             }
25268         };
25269
25270         var i = 0;
25271         for(; i < startingPos; i++) {
25272             textEls[i].innerHTML = (++prevStart);
25273             d.setDate(d.getDate()+1);
25274             cells[i].className = "x-date-prevday";
25275             setCellClass(this, cells[i]);
25276         }
25277         for(; i < days; i++){
25278             intDay = i - startingPos + 1;
25279             textEls[i].innerHTML = (intDay);
25280             d.setDate(d.getDate()+1);
25281             cells[i].className = "x-date-active";
25282             setCellClass(this, cells[i]);
25283         }
25284         var extraDays = 0;
25285         for(; i < 42; i++) {
25286              textEls[i].innerHTML = (++extraDays);
25287              d.setDate(d.getDate()+1);
25288              cells[i].className = "x-date-nextday";
25289              setCellClass(this, cells[i]);
25290         }
25291
25292         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25293         this.fireEvent('monthchange', this, date);
25294         
25295         if(!this.internalRender){
25296             var main = this.el.dom.firstChild;
25297             var w = main.offsetWidth;
25298             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25299             Roo.fly(main).setWidth(w);
25300             this.internalRender = true;
25301             // opera does not respect the auto grow header center column
25302             // then, after it gets a width opera refuses to recalculate
25303             // without a second pass
25304             if(Roo.isOpera && !this.secondPass){
25305                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25306                 this.secondPass = true;
25307                 this.update.defer(10, this, [date]);
25308             }
25309         }
25310         
25311         
25312     }
25313 });        /*
25314  * Based on:
25315  * Ext JS Library 1.1.1
25316  * Copyright(c) 2006-2007, Ext JS, LLC.
25317  *
25318  * Originally Released Under LGPL - original licence link has changed is not relivant.
25319  *
25320  * Fork - LGPL
25321  * <script type="text/javascript">
25322  */
25323 /**
25324  * @class Roo.TabPanel
25325  * @extends Roo.util.Observable
25326  * A lightweight tab container.
25327  * <br><br>
25328  * Usage:
25329  * <pre><code>
25330 // basic tabs 1, built from existing content
25331 var tabs = new Roo.TabPanel("tabs1");
25332 tabs.addTab("script", "View Script");
25333 tabs.addTab("markup", "View Markup");
25334 tabs.activate("script");
25335
25336 // more advanced tabs, built from javascript
25337 var jtabs = new Roo.TabPanel("jtabs");
25338 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25339
25340 // set up the UpdateManager
25341 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25342 var updater = tab2.getUpdateManager();
25343 updater.setDefaultUrl("ajax1.htm");
25344 tab2.on('activate', updater.refresh, updater, true);
25345
25346 // Use setUrl for Ajax loading
25347 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25348 tab3.setUrl("ajax2.htm", null, true);
25349
25350 // Disabled tab
25351 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25352 tab4.disable();
25353
25354 jtabs.activate("jtabs-1");
25355  * </code></pre>
25356  * @constructor
25357  * Create a new TabPanel.
25358  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25359  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25360  */
25361 Roo.TabPanel = function(container, config){
25362     /**
25363     * The container element for this TabPanel.
25364     * @type Roo.Element
25365     */
25366     this.el = Roo.get(container, true);
25367     if(config){
25368         if(typeof config == "boolean"){
25369             this.tabPosition = config ? "bottom" : "top";
25370         }else{
25371             Roo.apply(this, config);
25372         }
25373     }
25374     if(this.tabPosition == "bottom"){
25375         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25376         this.el.addClass("x-tabs-bottom");
25377     }
25378     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25379     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25380     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25381     if(Roo.isIE){
25382         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25383     }
25384     if(this.tabPosition != "bottom"){
25385         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25386          * @type Roo.Element
25387          */
25388         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25389         this.el.addClass("x-tabs-top");
25390     }
25391     this.items = [];
25392
25393     this.bodyEl.setStyle("position", "relative");
25394
25395     this.active = null;
25396     this.activateDelegate = this.activate.createDelegate(this);
25397
25398     this.addEvents({
25399         /**
25400          * @event tabchange
25401          * Fires when the active tab changes
25402          * @param {Roo.TabPanel} this
25403          * @param {Roo.TabPanelItem} activePanel The new active tab
25404          */
25405         "tabchange": true,
25406         /**
25407          * @event beforetabchange
25408          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25409          * @param {Roo.TabPanel} this
25410          * @param {Object} e Set cancel to true on this object to cancel the tab change
25411          * @param {Roo.TabPanelItem} tab The tab being changed to
25412          */
25413         "beforetabchange" : true
25414     });
25415
25416     Roo.EventManager.onWindowResize(this.onResize, this);
25417     this.cpad = this.el.getPadding("lr");
25418     this.hiddenCount = 0;
25419
25420
25421     // toolbar on the tabbar support...
25422     if (this.toolbar) {
25423         var tcfg = this.toolbar;
25424         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25425         this.toolbar = new Roo.Toolbar(tcfg);
25426         if (Roo.isSafari) {
25427             var tbl = tcfg.container.child('table', true);
25428             tbl.setAttribute('width', '100%');
25429         }
25430         
25431     }
25432    
25433
25434
25435     Roo.TabPanel.superclass.constructor.call(this);
25436 };
25437
25438 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25439     /*
25440      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25441      */
25442     tabPosition : "top",
25443     /*
25444      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25445      */
25446     currentTabWidth : 0,
25447     /*
25448      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25449      */
25450     minTabWidth : 40,
25451     /*
25452      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25453      */
25454     maxTabWidth : 250,
25455     /*
25456      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25457      */
25458     preferredTabWidth : 175,
25459     /*
25460      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25461      */
25462     resizeTabs : false,
25463     /*
25464      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25465      */
25466     monitorResize : true,
25467     /*
25468      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25469      */
25470     toolbar : false,
25471
25472     /**
25473      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25474      * @param {String} id The id of the div to use <b>or create</b>
25475      * @param {String} text The text for the tab
25476      * @param {String} content (optional) Content to put in the TabPanelItem body
25477      * @param {Boolean} closable (optional) True to create a close icon on the tab
25478      * @return {Roo.TabPanelItem} The created TabPanelItem
25479      */
25480     addTab : function(id, text, content, closable){
25481         var item = new Roo.TabPanelItem(this, id, text, closable);
25482         this.addTabItem(item);
25483         if(content){
25484             item.setContent(content);
25485         }
25486         return item;
25487     },
25488
25489     /**
25490      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25491      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25492      * @return {Roo.TabPanelItem}
25493      */
25494     getTab : function(id){
25495         return this.items[id];
25496     },
25497
25498     /**
25499      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25500      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25501      */
25502     hideTab : function(id){
25503         var t = this.items[id];
25504         if(!t.isHidden()){
25505            t.setHidden(true);
25506            this.hiddenCount++;
25507            this.autoSizeTabs();
25508         }
25509     },
25510
25511     /**
25512      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25513      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25514      */
25515     unhideTab : function(id){
25516         var t = this.items[id];
25517         if(t.isHidden()){
25518            t.setHidden(false);
25519            this.hiddenCount--;
25520            this.autoSizeTabs();
25521         }
25522     },
25523
25524     /**
25525      * Adds an existing {@link Roo.TabPanelItem}.
25526      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25527      */
25528     addTabItem : function(item){
25529         this.items[item.id] = item;
25530         this.items.push(item);
25531         if(this.resizeTabs){
25532            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25533            this.autoSizeTabs();
25534         }else{
25535             item.autoSize();
25536         }
25537     },
25538
25539     /**
25540      * Removes a {@link Roo.TabPanelItem}.
25541      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25542      */
25543     removeTab : function(id){
25544         var items = this.items;
25545         var tab = items[id];
25546         if(!tab) { return; }
25547         var index = items.indexOf(tab);
25548         if(this.active == tab && items.length > 1){
25549             var newTab = this.getNextAvailable(index);
25550             if(newTab) {
25551                 newTab.activate();
25552             }
25553         }
25554         this.stripEl.dom.removeChild(tab.pnode.dom);
25555         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25556             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25557         }
25558         items.splice(index, 1);
25559         delete this.items[tab.id];
25560         tab.fireEvent("close", tab);
25561         tab.purgeListeners();
25562         this.autoSizeTabs();
25563     },
25564
25565     getNextAvailable : function(start){
25566         var items = this.items;
25567         var index = start;
25568         // look for a next tab that will slide over to
25569         // replace the one being removed
25570         while(index < items.length){
25571             var item = items[++index];
25572             if(item && !item.isHidden()){
25573                 return item;
25574             }
25575         }
25576         // if one isn't found select the previous tab (on the left)
25577         index = start;
25578         while(index >= 0){
25579             var item = items[--index];
25580             if(item && !item.isHidden()){
25581                 return item;
25582             }
25583         }
25584         return null;
25585     },
25586
25587     /**
25588      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25589      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25590      */
25591     disableTab : function(id){
25592         var tab = this.items[id];
25593         if(tab && this.active != tab){
25594             tab.disable();
25595         }
25596     },
25597
25598     /**
25599      * Enables a {@link Roo.TabPanelItem} that is disabled.
25600      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25601      */
25602     enableTab : function(id){
25603         var tab = this.items[id];
25604         tab.enable();
25605     },
25606
25607     /**
25608      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25609      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25610      * @return {Roo.TabPanelItem} The TabPanelItem.
25611      */
25612     activate : function(id){
25613         var tab = this.items[id];
25614         if(!tab){
25615             return null;
25616         }
25617         if(tab == this.active || tab.disabled){
25618             return tab;
25619         }
25620         var e = {};
25621         this.fireEvent("beforetabchange", this, e, tab);
25622         if(e.cancel !== true && !tab.disabled){
25623             if(this.active){
25624                 this.active.hide();
25625             }
25626             this.active = this.items[id];
25627             this.active.show();
25628             this.fireEvent("tabchange", this, this.active);
25629         }
25630         return tab;
25631     },
25632
25633     /**
25634      * Gets the active {@link Roo.TabPanelItem}.
25635      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25636      */
25637     getActiveTab : function(){
25638         return this.active;
25639     },
25640
25641     /**
25642      * Updates the tab body element to fit the height of the container element
25643      * for overflow scrolling
25644      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25645      */
25646     syncHeight : function(targetHeight){
25647         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25648         var bm = this.bodyEl.getMargins();
25649         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25650         this.bodyEl.setHeight(newHeight);
25651         return newHeight;
25652     },
25653
25654     onResize : function(){
25655         if(this.monitorResize){
25656             this.autoSizeTabs();
25657         }
25658     },
25659
25660     /**
25661      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25662      */
25663     beginUpdate : function(){
25664         this.updating = true;
25665     },
25666
25667     /**
25668      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25669      */
25670     endUpdate : function(){
25671         this.updating = false;
25672         this.autoSizeTabs();
25673     },
25674
25675     /**
25676      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25677      */
25678     autoSizeTabs : function(){
25679         var count = this.items.length;
25680         var vcount = count - this.hiddenCount;
25681         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25682         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25683         var availWidth = Math.floor(w / vcount);
25684         var b = this.stripBody;
25685         if(b.getWidth() > w){
25686             var tabs = this.items;
25687             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25688             if(availWidth < this.minTabWidth){
25689                 /*if(!this.sleft){    // incomplete scrolling code
25690                     this.createScrollButtons();
25691                 }
25692                 this.showScroll();
25693                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25694             }
25695         }else{
25696             if(this.currentTabWidth < this.preferredTabWidth){
25697                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25698             }
25699         }
25700     },
25701
25702     /**
25703      * Returns the number of tabs in this TabPanel.
25704      * @return {Number}
25705      */
25706      getCount : function(){
25707          return this.items.length;
25708      },
25709
25710     /**
25711      * Resizes all the tabs to the passed width
25712      * @param {Number} The new width
25713      */
25714     setTabWidth : function(width){
25715         this.currentTabWidth = width;
25716         for(var i = 0, len = this.items.length; i < len; i++) {
25717                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25718         }
25719     },
25720
25721     /**
25722      * Destroys this TabPanel
25723      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25724      */
25725     destroy : function(removeEl){
25726         Roo.EventManager.removeResizeListener(this.onResize, this);
25727         for(var i = 0, len = this.items.length; i < len; i++){
25728             this.items[i].purgeListeners();
25729         }
25730         if(removeEl === true){
25731             this.el.update("");
25732             this.el.remove();
25733         }
25734     }
25735 });
25736
25737 /**
25738  * @class Roo.TabPanelItem
25739  * @extends Roo.util.Observable
25740  * Represents an individual item (tab plus body) in a TabPanel.
25741  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25742  * @param {String} id The id of this TabPanelItem
25743  * @param {String} text The text for the tab of this TabPanelItem
25744  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25745  */
25746 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25747     /**
25748      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25749      * @type Roo.TabPanel
25750      */
25751     this.tabPanel = tabPanel;
25752     /**
25753      * The id for this TabPanelItem
25754      * @type String
25755      */
25756     this.id = id;
25757     /** @private */
25758     this.disabled = false;
25759     /** @private */
25760     this.text = text;
25761     /** @private */
25762     this.loaded = false;
25763     this.closable = closable;
25764
25765     /**
25766      * The body element for this TabPanelItem.
25767      * @type Roo.Element
25768      */
25769     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25770     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25771     this.bodyEl.setStyle("display", "block");
25772     this.bodyEl.setStyle("zoom", "1");
25773     this.hideAction();
25774
25775     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25776     /** @private */
25777     this.el = Roo.get(els.el, true);
25778     this.inner = Roo.get(els.inner, true);
25779     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25780     this.pnode = Roo.get(els.el.parentNode, true);
25781     this.el.on("mousedown", this.onTabMouseDown, this);
25782     this.el.on("click", this.onTabClick, this);
25783     /** @private */
25784     if(closable){
25785         var c = Roo.get(els.close, true);
25786         c.dom.title = this.closeText;
25787         c.addClassOnOver("close-over");
25788         c.on("click", this.closeClick, this);
25789      }
25790
25791     this.addEvents({
25792          /**
25793          * @event activate
25794          * Fires when this tab becomes the active tab.
25795          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25796          * @param {Roo.TabPanelItem} this
25797          */
25798         "activate": true,
25799         /**
25800          * @event beforeclose
25801          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25802          * @param {Roo.TabPanelItem} this
25803          * @param {Object} e Set cancel to true on this object to cancel the close.
25804          */
25805         "beforeclose": true,
25806         /**
25807          * @event close
25808          * Fires when this tab is closed.
25809          * @param {Roo.TabPanelItem} this
25810          */
25811          "close": true,
25812         /**
25813          * @event deactivate
25814          * Fires when this tab is no longer the active tab.
25815          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25816          * @param {Roo.TabPanelItem} this
25817          */
25818          "deactivate" : true
25819     });
25820     this.hidden = false;
25821
25822     Roo.TabPanelItem.superclass.constructor.call(this);
25823 };
25824
25825 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25826     purgeListeners : function(){
25827        Roo.util.Observable.prototype.purgeListeners.call(this);
25828        this.el.removeAllListeners();
25829     },
25830     /**
25831      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25832      */
25833     show : function(){
25834         this.pnode.addClass("on");
25835         this.showAction();
25836         if(Roo.isOpera){
25837             this.tabPanel.stripWrap.repaint();
25838         }
25839         this.fireEvent("activate", this.tabPanel, this);
25840     },
25841
25842     /**
25843      * Returns true if this tab is the active tab.
25844      * @return {Boolean}
25845      */
25846     isActive : function(){
25847         return this.tabPanel.getActiveTab() == this;
25848     },
25849
25850     /**
25851      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25852      */
25853     hide : function(){
25854         this.pnode.removeClass("on");
25855         this.hideAction();
25856         this.fireEvent("deactivate", this.tabPanel, this);
25857     },
25858
25859     hideAction : function(){
25860         this.bodyEl.hide();
25861         this.bodyEl.setStyle("position", "absolute");
25862         this.bodyEl.setLeft("-20000px");
25863         this.bodyEl.setTop("-20000px");
25864     },
25865
25866     showAction : function(){
25867         this.bodyEl.setStyle("position", "relative");
25868         this.bodyEl.setTop("");
25869         this.bodyEl.setLeft("");
25870         this.bodyEl.show();
25871     },
25872
25873     /**
25874      * Set the tooltip for the tab.
25875      * @param {String} tooltip The tab's tooltip
25876      */
25877     setTooltip : function(text){
25878         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25879             this.textEl.dom.qtip = text;
25880             this.textEl.dom.removeAttribute('title');
25881         }else{
25882             this.textEl.dom.title = text;
25883         }
25884     },
25885
25886     onTabClick : function(e){
25887         e.preventDefault();
25888         this.tabPanel.activate(this.id);
25889     },
25890
25891     onTabMouseDown : function(e){
25892         e.preventDefault();
25893         this.tabPanel.activate(this.id);
25894     },
25895
25896     getWidth : function(){
25897         return this.inner.getWidth();
25898     },
25899
25900     setWidth : function(width){
25901         var iwidth = width - this.pnode.getPadding("lr");
25902         this.inner.setWidth(iwidth);
25903         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25904         this.pnode.setWidth(width);
25905     },
25906
25907     /**
25908      * Show or hide the tab
25909      * @param {Boolean} hidden True to hide or false to show.
25910      */
25911     setHidden : function(hidden){
25912         this.hidden = hidden;
25913         this.pnode.setStyle("display", hidden ? "none" : "");
25914     },
25915
25916     /**
25917      * Returns true if this tab is "hidden"
25918      * @return {Boolean}
25919      */
25920     isHidden : function(){
25921         return this.hidden;
25922     },
25923
25924     /**
25925      * Returns the text for this tab
25926      * @return {String}
25927      */
25928     getText : function(){
25929         return this.text;
25930     },
25931
25932     autoSize : function(){
25933         //this.el.beginMeasure();
25934         this.textEl.setWidth(1);
25935         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25936         //this.el.endMeasure();
25937     },
25938
25939     /**
25940      * Sets the text for the tab (Note: this also sets the tooltip text)
25941      * @param {String} text The tab's text and tooltip
25942      */
25943     setText : function(text){
25944         this.text = text;
25945         this.textEl.update(text);
25946         this.setTooltip(text);
25947         if(!this.tabPanel.resizeTabs){
25948             this.autoSize();
25949         }
25950     },
25951     /**
25952      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25953      */
25954     activate : function(){
25955         this.tabPanel.activate(this.id);
25956     },
25957
25958     /**
25959      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25960      */
25961     disable : function(){
25962         if(this.tabPanel.active != this){
25963             this.disabled = true;
25964             this.pnode.addClass("disabled");
25965         }
25966     },
25967
25968     /**
25969      * Enables this TabPanelItem if it was previously disabled.
25970      */
25971     enable : function(){
25972         this.disabled = false;
25973         this.pnode.removeClass("disabled");
25974     },
25975
25976     /**
25977      * Sets the content for this TabPanelItem.
25978      * @param {String} content The content
25979      * @param {Boolean} loadScripts true to look for and load scripts
25980      */
25981     setContent : function(content, loadScripts){
25982         this.bodyEl.update(content, loadScripts);
25983     },
25984
25985     /**
25986      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25987      * @return {Roo.UpdateManager} The UpdateManager
25988      */
25989     getUpdateManager : function(){
25990         return this.bodyEl.getUpdateManager();
25991     },
25992
25993     /**
25994      * Set a URL to be used to load the content for this TabPanelItem.
25995      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25996      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25997      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25998      * @return {Roo.UpdateManager} The UpdateManager
25999      */
26000     setUrl : function(url, params, loadOnce){
26001         if(this.refreshDelegate){
26002             this.un('activate', this.refreshDelegate);
26003         }
26004         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26005         this.on("activate", this.refreshDelegate);
26006         return this.bodyEl.getUpdateManager();
26007     },
26008
26009     /** @private */
26010     _handleRefresh : function(url, params, loadOnce){
26011         if(!loadOnce || !this.loaded){
26012             var updater = this.bodyEl.getUpdateManager();
26013             updater.update(url, params, this._setLoaded.createDelegate(this));
26014         }
26015     },
26016
26017     /**
26018      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26019      *   Will fail silently if the setUrl method has not been called.
26020      *   This does not activate the panel, just updates its content.
26021      */
26022     refresh : function(){
26023         if(this.refreshDelegate){
26024            this.loaded = false;
26025            this.refreshDelegate();
26026         }
26027     },
26028
26029     /** @private */
26030     _setLoaded : function(){
26031         this.loaded = true;
26032     },
26033
26034     /** @private */
26035     closeClick : function(e){
26036         var o = {};
26037         e.stopEvent();
26038         this.fireEvent("beforeclose", this, o);
26039         if(o.cancel !== true){
26040             this.tabPanel.removeTab(this.id);
26041         }
26042     },
26043     /**
26044      * The text displayed in the tooltip for the close icon.
26045      * @type String
26046      */
26047     closeText : "Close this tab"
26048 });
26049
26050 /** @private */
26051 Roo.TabPanel.prototype.createStrip = function(container){
26052     var strip = document.createElement("div");
26053     strip.className = "x-tabs-wrap";
26054     container.appendChild(strip);
26055     return strip;
26056 };
26057 /** @private */
26058 Roo.TabPanel.prototype.createStripList = function(strip){
26059     // div wrapper for retard IE
26060     // returns the "tr" element.
26061     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26062         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26063         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26064     return strip.firstChild.firstChild.firstChild.firstChild;
26065 };
26066 /** @private */
26067 Roo.TabPanel.prototype.createBody = function(container){
26068     var body = document.createElement("div");
26069     Roo.id(body, "tab-body");
26070     Roo.fly(body).addClass("x-tabs-body");
26071     container.appendChild(body);
26072     return body;
26073 };
26074 /** @private */
26075 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26076     var body = Roo.getDom(id);
26077     if(!body){
26078         body = document.createElement("div");
26079         body.id = id;
26080     }
26081     Roo.fly(body).addClass("x-tabs-item-body");
26082     bodyEl.insertBefore(body, bodyEl.firstChild);
26083     return body;
26084 };
26085 /** @private */
26086 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26087     var td = document.createElement("td");
26088     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26089     //stripEl.appendChild(td);
26090     if(closable){
26091         td.className = "x-tabs-closable";
26092         if(!this.closeTpl){
26093             this.closeTpl = new Roo.Template(
26094                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26095                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26096                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26097             );
26098         }
26099         var el = this.closeTpl.overwrite(td, {"text": text});
26100         var close = el.getElementsByTagName("div")[0];
26101         var inner = el.getElementsByTagName("em")[0];
26102         return {"el": el, "close": close, "inner": inner};
26103     } else {
26104         if(!this.tabTpl){
26105             this.tabTpl = new Roo.Template(
26106                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26107                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26108             );
26109         }
26110         var el = this.tabTpl.overwrite(td, {"text": text});
26111         var inner = el.getElementsByTagName("em")[0];
26112         return {"el": el, "inner": inner};
26113     }
26114 };/*
26115  * Based on:
26116  * Ext JS Library 1.1.1
26117  * Copyright(c) 2006-2007, Ext JS, LLC.
26118  *
26119  * Originally Released Under LGPL - original licence link has changed is not relivant.
26120  *
26121  * Fork - LGPL
26122  * <script type="text/javascript">
26123  */
26124
26125 /**
26126  * @class Roo.Button
26127  * @extends Roo.util.Observable
26128  * Simple Button class
26129  * @cfg {String} text The button text
26130  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26131  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26132  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26133  * @cfg {Object} scope The scope of the handler
26134  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26135  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26136  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26137  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26138  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26139  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26140    applies if enableToggle = true)
26141  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26142  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26143   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26144  * @constructor
26145  * Create a new button
26146  * @param {Object} config The config object
26147  */
26148 Roo.Button = function(renderTo, config)
26149 {
26150     if (!config) {
26151         config = renderTo;
26152         renderTo = config.renderTo || false;
26153     }
26154     
26155     Roo.apply(this, config);
26156     this.addEvents({
26157         /**
26158              * @event click
26159              * Fires when this button is clicked
26160              * @param {Button} this
26161              * @param {EventObject} e The click event
26162              */
26163             "click" : true,
26164         /**
26165              * @event toggle
26166              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26167              * @param {Button} this
26168              * @param {Boolean} pressed
26169              */
26170             "toggle" : true,
26171         /**
26172              * @event mouseover
26173              * Fires when the mouse hovers over the button
26174              * @param {Button} this
26175              * @param {Event} e The event object
26176              */
26177         'mouseover' : true,
26178         /**
26179              * @event mouseout
26180              * Fires when the mouse exits the button
26181              * @param {Button} this
26182              * @param {Event} e The event object
26183              */
26184         'mouseout': true,
26185          /**
26186              * @event render
26187              * Fires when the button is rendered
26188              * @param {Button} this
26189              */
26190         'render': true
26191     });
26192     if(this.menu){
26193         this.menu = Roo.menu.MenuMgr.get(this.menu);
26194     }
26195     // register listeners first!!  - so render can be captured..
26196     Roo.util.Observable.call(this);
26197     if(renderTo){
26198         this.render(renderTo);
26199     }
26200     
26201   
26202 };
26203
26204 Roo.extend(Roo.Button, Roo.util.Observable, {
26205     /**
26206      * 
26207      */
26208     
26209     /**
26210      * Read-only. True if this button is hidden
26211      * @type Boolean
26212      */
26213     hidden : false,
26214     /**
26215      * Read-only. True if this button is disabled
26216      * @type Boolean
26217      */
26218     disabled : false,
26219     /**
26220      * Read-only. True if this button is pressed (only if enableToggle = true)
26221      * @type Boolean
26222      */
26223     pressed : false,
26224
26225     /**
26226      * @cfg {Number} tabIndex 
26227      * The DOM tabIndex for this button (defaults to undefined)
26228      */
26229     tabIndex : undefined,
26230
26231     /**
26232      * @cfg {Boolean} enableToggle
26233      * True to enable pressed/not pressed toggling (defaults to false)
26234      */
26235     enableToggle: false,
26236     /**
26237      * @cfg {Mixed} menu
26238      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26239      */
26240     menu : undefined,
26241     /**
26242      * @cfg {String} menuAlign
26243      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26244      */
26245     menuAlign : "tl-bl?",
26246
26247     /**
26248      * @cfg {String} iconCls
26249      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26250      */
26251     iconCls : undefined,
26252     /**
26253      * @cfg {String} type
26254      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26255      */
26256     type : 'button',
26257
26258     // private
26259     menuClassTarget: 'tr',
26260
26261     /**
26262      * @cfg {String} clickEvent
26263      * The type of event to map to the button's event handler (defaults to 'click')
26264      */
26265     clickEvent : 'click',
26266
26267     /**
26268      * @cfg {Boolean} handleMouseEvents
26269      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26270      */
26271     handleMouseEvents : true,
26272
26273     /**
26274      * @cfg {String} tooltipType
26275      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26276      */
26277     tooltipType : 'qtip',
26278
26279     /**
26280      * @cfg {String} cls
26281      * A CSS class to apply to the button's main element.
26282      */
26283     
26284     /**
26285      * @cfg {Roo.Template} template (Optional)
26286      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26287      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26288      * require code modifications if required elements (e.g. a button) aren't present.
26289      */
26290
26291     // private
26292     render : function(renderTo){
26293         var btn;
26294         if(this.hideParent){
26295             this.parentEl = Roo.get(renderTo);
26296         }
26297         if(!this.dhconfig){
26298             if(!this.template){
26299                 if(!Roo.Button.buttonTemplate){
26300                     // hideous table template
26301                     Roo.Button.buttonTemplate = new Roo.Template(
26302                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26303                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
26304                         "</tr></tbody></table>");
26305                 }
26306                 this.template = Roo.Button.buttonTemplate;
26307             }
26308             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26309             var btnEl = btn.child("button:first");
26310             btnEl.on('focus', this.onFocus, this);
26311             btnEl.on('blur', this.onBlur, this);
26312             if(this.cls){
26313                 btn.addClass(this.cls);
26314             }
26315             if(this.icon){
26316                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26317             }
26318             if(this.iconCls){
26319                 btnEl.addClass(this.iconCls);
26320                 if(!this.cls){
26321                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26322                 }
26323             }
26324             if(this.tabIndex !== undefined){
26325                 btnEl.dom.tabIndex = this.tabIndex;
26326             }
26327             if(this.tooltip){
26328                 if(typeof this.tooltip == 'object'){
26329                     Roo.QuickTips.tips(Roo.apply({
26330                           target: btnEl.id
26331                     }, this.tooltip));
26332                 } else {
26333                     btnEl.dom[this.tooltipType] = this.tooltip;
26334                 }
26335             }
26336         }else{
26337             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26338         }
26339         this.el = btn;
26340         if(this.id){
26341             this.el.dom.id = this.el.id = this.id;
26342         }
26343         if(this.menu){
26344             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26345             this.menu.on("show", this.onMenuShow, this);
26346             this.menu.on("hide", this.onMenuHide, this);
26347         }
26348         btn.addClass("x-btn");
26349         if(Roo.isIE && !Roo.isIE7){
26350             this.autoWidth.defer(1, this);
26351         }else{
26352             this.autoWidth();
26353         }
26354         if(this.handleMouseEvents){
26355             btn.on("mouseover", this.onMouseOver, this);
26356             btn.on("mouseout", this.onMouseOut, this);
26357             btn.on("mousedown", this.onMouseDown, this);
26358         }
26359         btn.on(this.clickEvent, this.onClick, this);
26360         //btn.on("mouseup", this.onMouseUp, this);
26361         if(this.hidden){
26362             this.hide();
26363         }
26364         if(this.disabled){
26365             this.disable();
26366         }
26367         Roo.ButtonToggleMgr.register(this);
26368         if(this.pressed){
26369             this.el.addClass("x-btn-pressed");
26370         }
26371         if(this.repeat){
26372             var repeater = new Roo.util.ClickRepeater(btn,
26373                 typeof this.repeat == "object" ? this.repeat : {}
26374             );
26375             repeater.on("click", this.onClick,  this);
26376         }
26377         
26378         this.fireEvent('render', this);
26379         
26380     },
26381     /**
26382      * Returns the button's underlying element
26383      * @return {Roo.Element} The element
26384      */
26385     getEl : function(){
26386         return this.el;  
26387     },
26388     
26389     /**
26390      * Destroys this Button and removes any listeners.
26391      */
26392     destroy : function(){
26393         Roo.ButtonToggleMgr.unregister(this);
26394         this.el.removeAllListeners();
26395         this.purgeListeners();
26396         this.el.remove();
26397     },
26398
26399     // private
26400     autoWidth : function(){
26401         if(this.el){
26402             this.el.setWidth("auto");
26403             if(Roo.isIE7 && Roo.isStrict){
26404                 var ib = this.el.child('button');
26405                 if(ib && ib.getWidth() > 20){
26406                     ib.clip();
26407                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26408                 }
26409             }
26410             if(this.minWidth){
26411                 if(this.hidden){
26412                     this.el.beginMeasure();
26413                 }
26414                 if(this.el.getWidth() < this.minWidth){
26415                     this.el.setWidth(this.minWidth);
26416                 }
26417                 if(this.hidden){
26418                     this.el.endMeasure();
26419                 }
26420             }
26421         }
26422     },
26423
26424     /**
26425      * Assigns this button's click handler
26426      * @param {Function} handler The function to call when the button is clicked
26427      * @param {Object} scope (optional) Scope for the function passed in
26428      */
26429     setHandler : function(handler, scope){
26430         this.handler = handler;
26431         this.scope = scope;  
26432     },
26433     
26434     /**
26435      * Sets this button's text
26436      * @param {String} text The button text
26437      */
26438     setText : function(text){
26439         this.text = text;
26440         if(this.el){
26441             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26442         }
26443         this.autoWidth();
26444     },
26445     
26446     /**
26447      * Gets the text for this button
26448      * @return {String} The button text
26449      */
26450     getText : function(){
26451         return this.text;  
26452     },
26453     
26454     /**
26455      * Show this button
26456      */
26457     show: function(){
26458         this.hidden = false;
26459         if(this.el){
26460             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26461         }
26462     },
26463     
26464     /**
26465      * Hide this button
26466      */
26467     hide: function(){
26468         this.hidden = true;
26469         if(this.el){
26470             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26471         }
26472     },
26473     
26474     /**
26475      * Convenience function for boolean show/hide
26476      * @param {Boolean} visible True to show, false to hide
26477      */
26478     setVisible: function(visible){
26479         if(visible) {
26480             this.show();
26481         }else{
26482             this.hide();
26483         }
26484     },
26485     
26486     /**
26487      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26488      * @param {Boolean} state (optional) Force a particular state
26489      */
26490     toggle : function(state){
26491         state = state === undefined ? !this.pressed : state;
26492         if(state != this.pressed){
26493             if(state){
26494                 this.el.addClass("x-btn-pressed");
26495                 this.pressed = true;
26496                 this.fireEvent("toggle", this, true);
26497             }else{
26498                 this.el.removeClass("x-btn-pressed");
26499                 this.pressed = false;
26500                 this.fireEvent("toggle", this, false);
26501             }
26502             if(this.toggleHandler){
26503                 this.toggleHandler.call(this.scope || this, this, state);
26504             }
26505         }
26506     },
26507     
26508     /**
26509      * Focus the button
26510      */
26511     focus : function(){
26512         this.el.child('button:first').focus();
26513     },
26514     
26515     /**
26516      * Disable this button
26517      */
26518     disable : function(){
26519         if(this.el){
26520             this.el.addClass("x-btn-disabled");
26521         }
26522         this.disabled = true;
26523     },
26524     
26525     /**
26526      * Enable this button
26527      */
26528     enable : function(){
26529         if(this.el){
26530             this.el.removeClass("x-btn-disabled");
26531         }
26532         this.disabled = false;
26533     },
26534
26535     /**
26536      * Convenience function for boolean enable/disable
26537      * @param {Boolean} enabled True to enable, false to disable
26538      */
26539     setDisabled : function(v){
26540         this[v !== true ? "enable" : "disable"]();
26541     },
26542
26543     // private
26544     onClick : function(e){
26545         if(e){
26546             e.preventDefault();
26547         }
26548         if(e.button != 0){
26549             return;
26550         }
26551         if(!this.disabled){
26552             if(this.enableToggle){
26553                 this.toggle();
26554             }
26555             if(this.menu && !this.menu.isVisible()){
26556                 this.menu.show(this.el, this.menuAlign);
26557             }
26558             this.fireEvent("click", this, e);
26559             if(this.handler){
26560                 this.el.removeClass("x-btn-over");
26561                 this.handler.call(this.scope || this, this, e);
26562             }
26563         }
26564     },
26565     // private
26566     onMouseOver : function(e){
26567         if(!this.disabled){
26568             this.el.addClass("x-btn-over");
26569             this.fireEvent('mouseover', this, e);
26570         }
26571     },
26572     // private
26573     onMouseOut : function(e){
26574         if(!e.within(this.el,  true)){
26575             this.el.removeClass("x-btn-over");
26576             this.fireEvent('mouseout', this, e);
26577         }
26578     },
26579     // private
26580     onFocus : function(e){
26581         if(!this.disabled){
26582             this.el.addClass("x-btn-focus");
26583         }
26584     },
26585     // private
26586     onBlur : function(e){
26587         this.el.removeClass("x-btn-focus");
26588     },
26589     // private
26590     onMouseDown : function(e){
26591         if(!this.disabled && e.button == 0){
26592             this.el.addClass("x-btn-click");
26593             Roo.get(document).on('mouseup', this.onMouseUp, this);
26594         }
26595     },
26596     // private
26597     onMouseUp : function(e){
26598         if(e.button == 0){
26599             this.el.removeClass("x-btn-click");
26600             Roo.get(document).un('mouseup', this.onMouseUp, this);
26601         }
26602     },
26603     // private
26604     onMenuShow : function(e){
26605         this.el.addClass("x-btn-menu-active");
26606     },
26607     // private
26608     onMenuHide : function(e){
26609         this.el.removeClass("x-btn-menu-active");
26610     }   
26611 });
26612
26613 // Private utility class used by Button
26614 Roo.ButtonToggleMgr = function(){
26615    var groups = {};
26616    
26617    function toggleGroup(btn, state){
26618        if(state){
26619            var g = groups[btn.toggleGroup];
26620            for(var i = 0, l = g.length; i < l; i++){
26621                if(g[i] != btn){
26622                    g[i].toggle(false);
26623                }
26624            }
26625        }
26626    }
26627    
26628    return {
26629        register : function(btn){
26630            if(!btn.toggleGroup){
26631                return;
26632            }
26633            var g = groups[btn.toggleGroup];
26634            if(!g){
26635                g = groups[btn.toggleGroup] = [];
26636            }
26637            g.push(btn);
26638            btn.on("toggle", toggleGroup);
26639        },
26640        
26641        unregister : function(btn){
26642            if(!btn.toggleGroup){
26643                return;
26644            }
26645            var g = groups[btn.toggleGroup];
26646            if(g){
26647                g.remove(btn);
26648                btn.un("toggle", toggleGroup);
26649            }
26650        }
26651    };
26652 }();/*
26653  * Based on:
26654  * Ext JS Library 1.1.1
26655  * Copyright(c) 2006-2007, Ext JS, LLC.
26656  *
26657  * Originally Released Under LGPL - original licence link has changed is not relivant.
26658  *
26659  * Fork - LGPL
26660  * <script type="text/javascript">
26661  */
26662  
26663 /**
26664  * @class Roo.SplitButton
26665  * @extends Roo.Button
26666  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26667  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26668  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26669  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26670  * @cfg {String} arrowTooltip The title attribute of the arrow
26671  * @constructor
26672  * Create a new menu button
26673  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26674  * @param {Object} config The config object
26675  */
26676 Roo.SplitButton = function(renderTo, config){
26677     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26678     /**
26679      * @event arrowclick
26680      * Fires when this button's arrow is clicked
26681      * @param {SplitButton} this
26682      * @param {EventObject} e The click event
26683      */
26684     this.addEvents({"arrowclick":true});
26685 };
26686
26687 Roo.extend(Roo.SplitButton, Roo.Button, {
26688     render : function(renderTo){
26689         // this is one sweet looking template!
26690         var tpl = new Roo.Template(
26691             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26692             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26693             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26694             "</tbody></table></td><td>",
26695             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26696             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26697             "</tbody></table></td></tr></table>"
26698         );
26699         var btn = tpl.append(renderTo, [this.text, this.type], true);
26700         var btnEl = btn.child("button");
26701         if(this.cls){
26702             btn.addClass(this.cls);
26703         }
26704         if(this.icon){
26705             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26706         }
26707         if(this.iconCls){
26708             btnEl.addClass(this.iconCls);
26709             if(!this.cls){
26710                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26711             }
26712         }
26713         this.el = btn;
26714         if(this.handleMouseEvents){
26715             btn.on("mouseover", this.onMouseOver, this);
26716             btn.on("mouseout", this.onMouseOut, this);
26717             btn.on("mousedown", this.onMouseDown, this);
26718             btn.on("mouseup", this.onMouseUp, this);
26719         }
26720         btn.on(this.clickEvent, this.onClick, this);
26721         if(this.tooltip){
26722             if(typeof this.tooltip == 'object'){
26723                 Roo.QuickTips.tips(Roo.apply({
26724                       target: btnEl.id
26725                 }, this.tooltip));
26726             } else {
26727                 btnEl.dom[this.tooltipType] = this.tooltip;
26728             }
26729         }
26730         if(this.arrowTooltip){
26731             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26732         }
26733         if(this.hidden){
26734             this.hide();
26735         }
26736         if(this.disabled){
26737             this.disable();
26738         }
26739         if(this.pressed){
26740             this.el.addClass("x-btn-pressed");
26741         }
26742         if(Roo.isIE && !Roo.isIE7){
26743             this.autoWidth.defer(1, this);
26744         }else{
26745             this.autoWidth();
26746         }
26747         if(this.menu){
26748             this.menu.on("show", this.onMenuShow, this);
26749             this.menu.on("hide", this.onMenuHide, this);
26750         }
26751         this.fireEvent('render', this);
26752     },
26753
26754     // private
26755     autoWidth : function(){
26756         if(this.el){
26757             var tbl = this.el.child("table:first");
26758             var tbl2 = this.el.child("table:last");
26759             this.el.setWidth("auto");
26760             tbl.setWidth("auto");
26761             if(Roo.isIE7 && Roo.isStrict){
26762                 var ib = this.el.child('button:first');
26763                 if(ib && ib.getWidth() > 20){
26764                     ib.clip();
26765                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26766                 }
26767             }
26768             if(this.minWidth){
26769                 if(this.hidden){
26770                     this.el.beginMeasure();
26771                 }
26772                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26773                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26774                 }
26775                 if(this.hidden){
26776                     this.el.endMeasure();
26777                 }
26778             }
26779             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26780         } 
26781     },
26782     /**
26783      * Sets this button's click handler
26784      * @param {Function} handler The function to call when the button is clicked
26785      * @param {Object} scope (optional) Scope for the function passed above
26786      */
26787     setHandler : function(handler, scope){
26788         this.handler = handler;
26789         this.scope = scope;  
26790     },
26791     
26792     /**
26793      * Sets this button's arrow click handler
26794      * @param {Function} handler The function to call when the arrow is clicked
26795      * @param {Object} scope (optional) Scope for the function passed above
26796      */
26797     setArrowHandler : function(handler, scope){
26798         this.arrowHandler = handler;
26799         this.scope = scope;  
26800     },
26801     
26802     /**
26803      * Focus the button
26804      */
26805     focus : function(){
26806         if(this.el){
26807             this.el.child("button:first").focus();
26808         }
26809     },
26810
26811     // private
26812     onClick : function(e){
26813         e.preventDefault();
26814         if(!this.disabled){
26815             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26816                 if(this.menu && !this.menu.isVisible()){
26817                     this.menu.show(this.el, this.menuAlign);
26818                 }
26819                 this.fireEvent("arrowclick", this, e);
26820                 if(this.arrowHandler){
26821                     this.arrowHandler.call(this.scope || this, this, e);
26822                 }
26823             }else{
26824                 this.fireEvent("click", this, e);
26825                 if(this.handler){
26826                     this.handler.call(this.scope || this, this, e);
26827                 }
26828             }
26829         }
26830     },
26831     // private
26832     onMouseDown : function(e){
26833         if(!this.disabled){
26834             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26835         }
26836     },
26837     // private
26838     onMouseUp : function(e){
26839         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26840     }   
26841 });
26842
26843
26844 // backwards compat
26845 Roo.MenuButton = Roo.SplitButton;/*
26846  * Based on:
26847  * Ext JS Library 1.1.1
26848  * Copyright(c) 2006-2007, Ext JS, LLC.
26849  *
26850  * Originally Released Under LGPL - original licence link has changed is not relivant.
26851  *
26852  * Fork - LGPL
26853  * <script type="text/javascript">
26854  */
26855
26856 /**
26857  * @class Roo.Toolbar
26858  * Basic Toolbar class.
26859  * @constructor
26860  * Creates a new Toolbar
26861  * @param {Object} container The config object
26862  */ 
26863 Roo.Toolbar = function(container, buttons, config)
26864 {
26865     /// old consturctor format still supported..
26866     if(container instanceof Array){ // omit the container for later rendering
26867         buttons = container;
26868         config = buttons;
26869         container = null;
26870     }
26871     if (typeof(container) == 'object' && container.xtype) {
26872         config = container;
26873         container = config.container;
26874         buttons = config.buttons || []; // not really - use items!!
26875     }
26876     var xitems = [];
26877     if (config && config.items) {
26878         xitems = config.items;
26879         delete config.items;
26880     }
26881     Roo.apply(this, config);
26882     this.buttons = buttons;
26883     
26884     if(container){
26885         this.render(container);
26886     }
26887     this.xitems = xitems;
26888     Roo.each(xitems, function(b) {
26889         this.add(b);
26890     }, this);
26891     
26892 };
26893
26894 Roo.Toolbar.prototype = {
26895     /**
26896      * @cfg {Array} items
26897      * array of button configs or elements to add (will be converted to a MixedCollection)
26898      */
26899     
26900     /**
26901      * @cfg {String/HTMLElement/Element} container
26902      * The id or element that will contain the toolbar
26903      */
26904     // private
26905     render : function(ct){
26906         this.el = Roo.get(ct);
26907         if(this.cls){
26908             this.el.addClass(this.cls);
26909         }
26910         // using a table allows for vertical alignment
26911         // 100% width is needed by Safari...
26912         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26913         this.tr = this.el.child("tr", true);
26914         var autoId = 0;
26915         this.items = new Roo.util.MixedCollection(false, function(o){
26916             return o.id || ("item" + (++autoId));
26917         });
26918         if(this.buttons){
26919             this.add.apply(this, this.buttons);
26920             delete this.buttons;
26921         }
26922     },
26923
26924     /**
26925      * Adds element(s) to the toolbar -- this function takes a variable number of 
26926      * arguments of mixed type and adds them to the toolbar.
26927      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26928      * <ul>
26929      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26930      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26931      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26932      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26933      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26934      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26935      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26936      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26937      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26938      * </ul>
26939      * @param {Mixed} arg2
26940      * @param {Mixed} etc.
26941      */
26942     add : function(){
26943         var a = arguments, l = a.length;
26944         for(var i = 0; i < l; i++){
26945             this._add(a[i]);
26946         }
26947     },
26948     // private..
26949     _add : function(el) {
26950         
26951         if (el.xtype) {
26952             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26953         }
26954         
26955         if (el.applyTo){ // some kind of form field
26956             return this.addField(el);
26957         } 
26958         if (el.render){ // some kind of Toolbar.Item
26959             return this.addItem(el);
26960         }
26961         if (typeof el == "string"){ // string
26962             if(el == "separator" || el == "-"){
26963                 return this.addSeparator();
26964             }
26965             if (el == " "){
26966                 return this.addSpacer();
26967             }
26968             if(el == "->"){
26969                 return this.addFill();
26970             }
26971             return this.addText(el);
26972             
26973         }
26974         if(el.tagName){ // element
26975             return this.addElement(el);
26976         }
26977         if(typeof el == "object"){ // must be button config?
26978             return this.addButton(el);
26979         }
26980         // and now what?!?!
26981         return false;
26982         
26983     },
26984     
26985     /**
26986      * Add an Xtype element
26987      * @param {Object} xtype Xtype Object
26988      * @return {Object} created Object
26989      */
26990     addxtype : function(e){
26991         return this.add(e);  
26992     },
26993     
26994     /**
26995      * Returns the Element for this toolbar.
26996      * @return {Roo.Element}
26997      */
26998     getEl : function(){
26999         return this.el;  
27000     },
27001     
27002     /**
27003      * Adds a separator
27004      * @return {Roo.Toolbar.Item} The separator item
27005      */
27006     addSeparator : function(){
27007         return this.addItem(new Roo.Toolbar.Separator());
27008     },
27009
27010     /**
27011      * Adds a spacer element
27012      * @return {Roo.Toolbar.Spacer} The spacer item
27013      */
27014     addSpacer : function(){
27015         return this.addItem(new Roo.Toolbar.Spacer());
27016     },
27017
27018     /**
27019      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27020      * @return {Roo.Toolbar.Fill} The fill item
27021      */
27022     addFill : function(){
27023         return this.addItem(new Roo.Toolbar.Fill());
27024     },
27025
27026     /**
27027      * Adds any standard HTML element to the toolbar
27028      * @param {String/HTMLElement/Element} el The element or id of the element to add
27029      * @return {Roo.Toolbar.Item} The element's item
27030      */
27031     addElement : function(el){
27032         return this.addItem(new Roo.Toolbar.Item(el));
27033     },
27034     /**
27035      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27036      * @type Roo.util.MixedCollection  
27037      */
27038     items : false,
27039      
27040     /**
27041      * Adds any Toolbar.Item or subclass
27042      * @param {Roo.Toolbar.Item} item
27043      * @return {Roo.Toolbar.Item} The item
27044      */
27045     addItem : function(item){
27046         var td = this.nextBlock();
27047         item.render(td);
27048         this.items.add(item);
27049         return item;
27050     },
27051     
27052     /**
27053      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27054      * @param {Object/Array} config A button config or array of configs
27055      * @return {Roo.Toolbar.Button/Array}
27056      */
27057     addButton : function(config){
27058         if(config instanceof Array){
27059             var buttons = [];
27060             for(var i = 0, len = config.length; i < len; i++) {
27061                 buttons.push(this.addButton(config[i]));
27062             }
27063             return buttons;
27064         }
27065         var b = config;
27066         if(!(config instanceof Roo.Toolbar.Button)){
27067             b = config.split ?
27068                 new Roo.Toolbar.SplitButton(config) :
27069                 new Roo.Toolbar.Button(config);
27070         }
27071         var td = this.nextBlock();
27072         b.render(td);
27073         this.items.add(b);
27074         return b;
27075     },
27076     
27077     /**
27078      * Adds text to the toolbar
27079      * @param {String} text The text to add
27080      * @return {Roo.Toolbar.Item} The element's item
27081      */
27082     addText : function(text){
27083         return this.addItem(new Roo.Toolbar.TextItem(text));
27084     },
27085     
27086     /**
27087      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27088      * @param {Number} index The index where the item is to be inserted
27089      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27090      * @return {Roo.Toolbar.Button/Item}
27091      */
27092     insertButton : function(index, item){
27093         if(item instanceof Array){
27094             var buttons = [];
27095             for(var i = 0, len = item.length; i < len; i++) {
27096                buttons.push(this.insertButton(index + i, item[i]));
27097             }
27098             return buttons;
27099         }
27100         if (!(item instanceof Roo.Toolbar.Button)){
27101            item = new Roo.Toolbar.Button(item);
27102         }
27103         var td = document.createElement("td");
27104         this.tr.insertBefore(td, this.tr.childNodes[index]);
27105         item.render(td);
27106         this.items.insert(index, item);
27107         return item;
27108     },
27109     
27110     /**
27111      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27112      * @param {Object} config
27113      * @return {Roo.Toolbar.Item} The element's item
27114      */
27115     addDom : function(config, returnEl){
27116         var td = this.nextBlock();
27117         Roo.DomHelper.overwrite(td, config);
27118         var ti = new Roo.Toolbar.Item(td.firstChild);
27119         ti.render(td);
27120         this.items.add(ti);
27121         return ti;
27122     },
27123
27124     /**
27125      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27126      * @type Roo.util.MixedCollection  
27127      */
27128     fields : false,
27129     
27130     /**
27131      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27132      * Note: the field should not have been rendered yet. For a field that has already been
27133      * rendered, use {@link #addElement}.
27134      * @param {Roo.form.Field} field
27135      * @return {Roo.ToolbarItem}
27136      */
27137      
27138       
27139     addField : function(field) {
27140         if (!this.fields) {
27141             var autoId = 0;
27142             this.fields = new Roo.util.MixedCollection(false, function(o){
27143                 return o.id || ("item" + (++autoId));
27144             });
27145
27146         }
27147         
27148         var td = this.nextBlock();
27149         field.render(td);
27150         var ti = new Roo.Toolbar.Item(td.firstChild);
27151         ti.render(td);
27152         this.items.add(ti);
27153         this.fields.add(field);
27154         return ti;
27155     },
27156     /**
27157      * Hide the toolbar
27158      * @method hide
27159      */
27160      
27161       
27162     hide : function()
27163     {
27164         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27165         this.el.child('div').hide();
27166     },
27167     /**
27168      * Show the toolbar
27169      * @method show
27170      */
27171     show : function()
27172     {
27173         this.el.child('div').show();
27174     },
27175       
27176     // private
27177     nextBlock : function(){
27178         var td = document.createElement("td");
27179         this.tr.appendChild(td);
27180         return td;
27181     },
27182
27183     // private
27184     destroy : function(){
27185         if(this.items){ // rendered?
27186             Roo.destroy.apply(Roo, this.items.items);
27187         }
27188         if(this.fields){ // rendered?
27189             Roo.destroy.apply(Roo, this.fields.items);
27190         }
27191         Roo.Element.uncache(this.el, this.tr);
27192     }
27193 };
27194
27195 /**
27196  * @class Roo.Toolbar.Item
27197  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27198  * @constructor
27199  * Creates a new Item
27200  * @param {HTMLElement} el 
27201  */
27202 Roo.Toolbar.Item = function(el){
27203     this.el = Roo.getDom(el);
27204     this.id = Roo.id(this.el);
27205     this.hidden = false;
27206 };
27207
27208 Roo.Toolbar.Item.prototype = {
27209     
27210     /**
27211      * Get this item's HTML Element
27212      * @return {HTMLElement}
27213      */
27214     getEl : function(){
27215        return this.el;  
27216     },
27217
27218     // private
27219     render : function(td){
27220         this.td = td;
27221         td.appendChild(this.el);
27222     },
27223     
27224     /**
27225      * Removes and destroys this item.
27226      */
27227     destroy : function(){
27228         this.td.parentNode.removeChild(this.td);
27229     },
27230     
27231     /**
27232      * Shows this item.
27233      */
27234     show: function(){
27235         this.hidden = false;
27236         this.td.style.display = "";
27237     },
27238     
27239     /**
27240      * Hides this item.
27241      */
27242     hide: function(){
27243         this.hidden = true;
27244         this.td.style.display = "none";
27245     },
27246     
27247     /**
27248      * Convenience function for boolean show/hide.
27249      * @param {Boolean} visible true to show/false to hide
27250      */
27251     setVisible: function(visible){
27252         if(visible) {
27253             this.show();
27254         }else{
27255             this.hide();
27256         }
27257     },
27258     
27259     /**
27260      * Try to focus this item.
27261      */
27262     focus : function(){
27263         Roo.fly(this.el).focus();
27264     },
27265     
27266     /**
27267      * Disables this item.
27268      */
27269     disable : function(){
27270         Roo.fly(this.td).addClass("x-item-disabled");
27271         this.disabled = true;
27272         this.el.disabled = true;
27273     },
27274     
27275     /**
27276      * Enables this item.
27277      */
27278     enable : function(){
27279         Roo.fly(this.td).removeClass("x-item-disabled");
27280         this.disabled = false;
27281         this.el.disabled = false;
27282     }
27283 };
27284
27285
27286 /**
27287  * @class Roo.Toolbar.Separator
27288  * @extends Roo.Toolbar.Item
27289  * A simple toolbar separator class
27290  * @constructor
27291  * Creates a new Separator
27292  */
27293 Roo.Toolbar.Separator = function(){
27294     var s = document.createElement("span");
27295     s.className = "ytb-sep";
27296     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27297 };
27298 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27299     enable:Roo.emptyFn,
27300     disable:Roo.emptyFn,
27301     focus:Roo.emptyFn
27302 });
27303
27304 /**
27305  * @class Roo.Toolbar.Spacer
27306  * @extends Roo.Toolbar.Item
27307  * A simple element that adds extra horizontal space to a toolbar.
27308  * @constructor
27309  * Creates a new Spacer
27310  */
27311 Roo.Toolbar.Spacer = function(){
27312     var s = document.createElement("div");
27313     s.className = "ytb-spacer";
27314     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27315 };
27316 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27317     enable:Roo.emptyFn,
27318     disable:Roo.emptyFn,
27319     focus:Roo.emptyFn
27320 });
27321
27322 /**
27323  * @class Roo.Toolbar.Fill
27324  * @extends Roo.Toolbar.Spacer
27325  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27326  * @constructor
27327  * Creates a new Spacer
27328  */
27329 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27330     // private
27331     render : function(td){
27332         td.style.width = '100%';
27333         Roo.Toolbar.Fill.superclass.render.call(this, td);
27334     }
27335 });
27336
27337 /**
27338  * @class Roo.Toolbar.TextItem
27339  * @extends Roo.Toolbar.Item
27340  * A simple class that renders text directly into a toolbar.
27341  * @constructor
27342  * Creates a new TextItem
27343  * @param {String} text
27344  */
27345 Roo.Toolbar.TextItem = function(text){
27346     if (typeof(text) == 'object') {
27347         text = text.text;
27348     }
27349     var s = document.createElement("span");
27350     s.className = "ytb-text";
27351     s.innerHTML = text;
27352     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27353 };
27354 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27355     enable:Roo.emptyFn,
27356     disable:Roo.emptyFn,
27357     focus:Roo.emptyFn
27358 });
27359
27360 /**
27361  * @class Roo.Toolbar.Button
27362  * @extends Roo.Button
27363  * A button that renders into a toolbar.
27364  * @constructor
27365  * Creates a new Button
27366  * @param {Object} config A standard {@link Roo.Button} config object
27367  */
27368 Roo.Toolbar.Button = function(config){
27369     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27370 };
27371 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27372     render : function(td){
27373         this.td = td;
27374         Roo.Toolbar.Button.superclass.render.call(this, td);
27375     },
27376     
27377     /**
27378      * Removes and destroys this button
27379      */
27380     destroy : function(){
27381         Roo.Toolbar.Button.superclass.destroy.call(this);
27382         this.td.parentNode.removeChild(this.td);
27383     },
27384     
27385     /**
27386      * Shows this button
27387      */
27388     show: function(){
27389         this.hidden = false;
27390         this.td.style.display = "";
27391     },
27392     
27393     /**
27394      * Hides this button
27395      */
27396     hide: function(){
27397         this.hidden = true;
27398         this.td.style.display = "none";
27399     },
27400
27401     /**
27402      * Disables this item
27403      */
27404     disable : function(){
27405         Roo.fly(this.td).addClass("x-item-disabled");
27406         this.disabled = true;
27407     },
27408
27409     /**
27410      * Enables this item
27411      */
27412     enable : function(){
27413         Roo.fly(this.td).removeClass("x-item-disabled");
27414         this.disabled = false;
27415     }
27416 });
27417 // backwards compat
27418 Roo.ToolbarButton = Roo.Toolbar.Button;
27419
27420 /**
27421  * @class Roo.Toolbar.SplitButton
27422  * @extends Roo.SplitButton
27423  * A menu button that renders into a toolbar.
27424  * @constructor
27425  * Creates a new SplitButton
27426  * @param {Object} config A standard {@link Roo.SplitButton} config object
27427  */
27428 Roo.Toolbar.SplitButton = function(config){
27429     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27430 };
27431 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27432     render : function(td){
27433         this.td = td;
27434         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27435     },
27436     
27437     /**
27438      * Removes and destroys this button
27439      */
27440     destroy : function(){
27441         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27442         this.td.parentNode.removeChild(this.td);
27443     },
27444     
27445     /**
27446      * Shows this button
27447      */
27448     show: function(){
27449         this.hidden = false;
27450         this.td.style.display = "";
27451     },
27452     
27453     /**
27454      * Hides this button
27455      */
27456     hide: function(){
27457         this.hidden = true;
27458         this.td.style.display = "none";
27459     }
27460 });
27461
27462 // backwards compat
27463 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27464  * Based on:
27465  * Ext JS Library 1.1.1
27466  * Copyright(c) 2006-2007, Ext JS, LLC.
27467  *
27468  * Originally Released Under LGPL - original licence link has changed is not relivant.
27469  *
27470  * Fork - LGPL
27471  * <script type="text/javascript">
27472  */
27473  
27474 /**
27475  * @class Roo.PagingToolbar
27476  * @extends Roo.Toolbar
27477  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27478  * @constructor
27479  * Create a new PagingToolbar
27480  * @param {Object} config The config object
27481  */
27482 Roo.PagingToolbar = function(el, ds, config)
27483 {
27484     // old args format still supported... - xtype is prefered..
27485     if (typeof(el) == 'object' && el.xtype) {
27486         // created from xtype...
27487         config = el;
27488         ds = el.dataSource;
27489         el = config.container;
27490     }
27491     var items = [];
27492     if (config.items) {
27493         items = config.items;
27494         config.items = [];
27495     }
27496     
27497     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27498     this.ds = ds;
27499     this.cursor = 0;
27500     this.renderButtons(this.el);
27501     this.bind(ds);
27502     
27503     // supprot items array.
27504    
27505     Roo.each(items, function(e) {
27506         this.add(Roo.factory(e));
27507     },this);
27508     
27509 };
27510
27511 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27512     /**
27513      * @cfg {Roo.data.Store} dataSource
27514      * The underlying data store providing the paged data
27515      */
27516     /**
27517      * @cfg {String/HTMLElement/Element} container
27518      * container The id or element that will contain the toolbar
27519      */
27520     /**
27521      * @cfg {Boolean} displayInfo
27522      * True to display the displayMsg (defaults to false)
27523      */
27524     /**
27525      * @cfg {Number} pageSize
27526      * The number of records to display per page (defaults to 20)
27527      */
27528     pageSize: 20,
27529     /**
27530      * @cfg {String} displayMsg
27531      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27532      */
27533     displayMsg : 'Displaying {0} - {1} of {2}',
27534     /**
27535      * @cfg {String} emptyMsg
27536      * The message to display when no records are found (defaults to "No data to display")
27537      */
27538     emptyMsg : 'No data to display',
27539     /**
27540      * Customizable piece of the default paging text (defaults to "Page")
27541      * @type String
27542      */
27543     beforePageText : "Page",
27544     /**
27545      * Customizable piece of the default paging text (defaults to "of %0")
27546      * @type String
27547      */
27548     afterPageText : "of {0}",
27549     /**
27550      * Customizable piece of the default paging text (defaults to "First Page")
27551      * @type String
27552      */
27553     firstText : "First Page",
27554     /**
27555      * Customizable piece of the default paging text (defaults to "Previous Page")
27556      * @type String
27557      */
27558     prevText : "Previous Page",
27559     /**
27560      * Customizable piece of the default paging text (defaults to "Next Page")
27561      * @type String
27562      */
27563     nextText : "Next Page",
27564     /**
27565      * Customizable piece of the default paging text (defaults to "Last Page")
27566      * @type String
27567      */
27568     lastText : "Last Page",
27569     /**
27570      * Customizable piece of the default paging text (defaults to "Refresh")
27571      * @type String
27572      */
27573     refreshText : "Refresh",
27574
27575     // private
27576     renderButtons : function(el){
27577         Roo.PagingToolbar.superclass.render.call(this, el);
27578         this.first = this.addButton({
27579             tooltip: this.firstText,
27580             cls: "x-btn-icon x-grid-page-first",
27581             disabled: true,
27582             handler: this.onClick.createDelegate(this, ["first"])
27583         });
27584         this.prev = this.addButton({
27585             tooltip: this.prevText,
27586             cls: "x-btn-icon x-grid-page-prev",
27587             disabled: true,
27588             handler: this.onClick.createDelegate(this, ["prev"])
27589         });
27590         //this.addSeparator();
27591         this.add(this.beforePageText);
27592         this.field = Roo.get(this.addDom({
27593            tag: "input",
27594            type: "text",
27595            size: "3",
27596            value: "1",
27597            cls: "x-grid-page-number"
27598         }).el);
27599         this.field.on("keydown", this.onPagingKeydown, this);
27600         this.field.on("focus", function(){this.dom.select();});
27601         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27602         this.field.setHeight(18);
27603         //this.addSeparator();
27604         this.next = this.addButton({
27605             tooltip: this.nextText,
27606             cls: "x-btn-icon x-grid-page-next",
27607             disabled: true,
27608             handler: this.onClick.createDelegate(this, ["next"])
27609         });
27610         this.last = this.addButton({
27611             tooltip: this.lastText,
27612             cls: "x-btn-icon x-grid-page-last",
27613             disabled: true,
27614             handler: this.onClick.createDelegate(this, ["last"])
27615         });
27616         //this.addSeparator();
27617         this.loading = this.addButton({
27618             tooltip: this.refreshText,
27619             cls: "x-btn-icon x-grid-loading",
27620             handler: this.onClick.createDelegate(this, ["refresh"])
27621         });
27622
27623         if(this.displayInfo){
27624             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27625         }
27626     },
27627
27628     // private
27629     updateInfo : function(){
27630         if(this.displayEl){
27631             var count = this.ds.getCount();
27632             var msg = count == 0 ?
27633                 this.emptyMsg :
27634                 String.format(
27635                     this.displayMsg,
27636                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27637                 );
27638             this.displayEl.update(msg);
27639         }
27640     },
27641
27642     // private
27643     onLoad : function(ds, r, o){
27644        this.cursor = o.params ? o.params.start : 0;
27645        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27646
27647        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27648        this.field.dom.value = ap;
27649        this.first.setDisabled(ap == 1);
27650        this.prev.setDisabled(ap == 1);
27651        this.next.setDisabled(ap == ps);
27652        this.last.setDisabled(ap == ps);
27653        this.loading.enable();
27654        this.updateInfo();
27655     },
27656
27657     // private
27658     getPageData : function(){
27659         var total = this.ds.getTotalCount();
27660         return {
27661             total : total,
27662             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27663             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27664         };
27665     },
27666
27667     // private
27668     onLoadError : function(){
27669         this.loading.enable();
27670     },
27671
27672     // private
27673     onPagingKeydown : function(e){
27674         var k = e.getKey();
27675         var d = this.getPageData();
27676         if(k == e.RETURN){
27677             var v = this.field.dom.value, pageNum;
27678             if(!v || isNaN(pageNum = parseInt(v, 10))){
27679                 this.field.dom.value = d.activePage;
27680                 return;
27681             }
27682             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27683             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27684             e.stopEvent();
27685         }
27686         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27687         {
27688           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27689           this.field.dom.value = pageNum;
27690           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27691           e.stopEvent();
27692         }
27693         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27694         {
27695           var v = this.field.dom.value, pageNum; 
27696           var increment = (e.shiftKey) ? 10 : 1;
27697           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27698             increment *= -1;
27699           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27700             this.field.dom.value = d.activePage;
27701             return;
27702           }
27703           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27704           {
27705             this.field.dom.value = parseInt(v, 10) + increment;
27706             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27707             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27708           }
27709           e.stopEvent();
27710         }
27711     },
27712
27713     // private
27714     beforeLoad : function(){
27715         if(this.loading){
27716             this.loading.disable();
27717         }
27718     },
27719
27720     // private
27721     onClick : function(which){
27722         var ds = this.ds;
27723         switch(which){
27724             case "first":
27725                 ds.load({params:{start: 0, limit: this.pageSize}});
27726             break;
27727             case "prev":
27728                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27729             break;
27730             case "next":
27731                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27732             break;
27733             case "last":
27734                 var total = ds.getTotalCount();
27735                 var extra = total % this.pageSize;
27736                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27737                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27738             break;
27739             case "refresh":
27740                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27741             break;
27742         }
27743     },
27744
27745     /**
27746      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27747      * @param {Roo.data.Store} store The data store to unbind
27748      */
27749     unbind : function(ds){
27750         ds.un("beforeload", this.beforeLoad, this);
27751         ds.un("load", this.onLoad, this);
27752         ds.un("loadexception", this.onLoadError, this);
27753         ds.un("remove", this.updateInfo, this);
27754         ds.un("add", this.updateInfo, this);
27755         this.ds = undefined;
27756     },
27757
27758     /**
27759      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27760      * @param {Roo.data.Store} store The data store to bind
27761      */
27762     bind : function(ds){
27763         ds.on("beforeload", this.beforeLoad, this);
27764         ds.on("load", this.onLoad, this);
27765         ds.on("loadexception", this.onLoadError, this);
27766         ds.on("remove", this.updateInfo, this);
27767         ds.on("add", this.updateInfo, this);
27768         this.ds = ds;
27769     }
27770 });/*
27771  * Based on:
27772  * Ext JS Library 1.1.1
27773  * Copyright(c) 2006-2007, Ext JS, LLC.
27774  *
27775  * Originally Released Under LGPL - original licence link has changed is not relivant.
27776  *
27777  * Fork - LGPL
27778  * <script type="text/javascript">
27779  */
27780
27781 /**
27782  * @class Roo.Resizable
27783  * @extends Roo.util.Observable
27784  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27785  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27786  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27787  * the element will be wrapped for you automatically.</p>
27788  * <p>Here is the list of valid resize handles:</p>
27789  * <pre>
27790 Value   Description
27791 ------  -------------------
27792  'n'     north
27793  's'     south
27794  'e'     east
27795  'w'     west
27796  'nw'    northwest
27797  'sw'    southwest
27798  'se'    southeast
27799  'ne'    northeast
27800  'hd'    horizontal drag
27801  'all'   all
27802 </pre>
27803  * <p>Here's an example showing the creation of a typical Resizable:</p>
27804  * <pre><code>
27805 var resizer = new Roo.Resizable("element-id", {
27806     handles: 'all',
27807     minWidth: 200,
27808     minHeight: 100,
27809     maxWidth: 500,
27810     maxHeight: 400,
27811     pinned: true
27812 });
27813 resizer.on("resize", myHandler);
27814 </code></pre>
27815  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27816  * resizer.east.setDisplayed(false);</p>
27817  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27818  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27819  * resize operation's new size (defaults to [0, 0])
27820  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27821  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27822  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27823  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27824  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27825  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27826  * @cfg {Number} width The width of the element in pixels (defaults to null)
27827  * @cfg {Number} height The height of the element in pixels (defaults to null)
27828  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27829  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27830  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27831  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27832  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27833  * in favor of the handles config option (defaults to false)
27834  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27835  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27836  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27837  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27838  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27839  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27840  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27841  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27842  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27843  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27844  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27845  * @constructor
27846  * Create a new resizable component
27847  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27848  * @param {Object} config configuration options
27849   */
27850 Roo.Resizable = function(el, config)
27851 {
27852     this.el = Roo.get(el);
27853
27854     if(config && config.wrap){
27855         config.resizeChild = this.el;
27856         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27857         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27858         this.el.setStyle("overflow", "hidden");
27859         this.el.setPositioning(config.resizeChild.getPositioning());
27860         config.resizeChild.clearPositioning();
27861         if(!config.width || !config.height){
27862             var csize = config.resizeChild.getSize();
27863             this.el.setSize(csize.width, csize.height);
27864         }
27865         if(config.pinned && !config.adjustments){
27866             config.adjustments = "auto";
27867         }
27868     }
27869
27870     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27871     this.proxy.unselectable();
27872     this.proxy.enableDisplayMode('block');
27873
27874     Roo.apply(this, config);
27875
27876     if(this.pinned){
27877         this.disableTrackOver = true;
27878         this.el.addClass("x-resizable-pinned");
27879     }
27880     // if the element isn't positioned, make it relative
27881     var position = this.el.getStyle("position");
27882     if(position != "absolute" && position != "fixed"){
27883         this.el.setStyle("position", "relative");
27884     }
27885     if(!this.handles){ // no handles passed, must be legacy style
27886         this.handles = 's,e,se';
27887         if(this.multiDirectional){
27888             this.handles += ',n,w';
27889         }
27890     }
27891     if(this.handles == "all"){
27892         this.handles = "n s e w ne nw se sw";
27893     }
27894     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27895     var ps = Roo.Resizable.positions;
27896     for(var i = 0, len = hs.length; i < len; i++){
27897         if(hs[i] && ps[hs[i]]){
27898             var pos = ps[hs[i]];
27899             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27900         }
27901     }
27902     // legacy
27903     this.corner = this.southeast;
27904     
27905     // updateBox = the box can move..
27906     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27907         this.updateBox = true;
27908     }
27909
27910     this.activeHandle = null;
27911
27912     if(this.resizeChild){
27913         if(typeof this.resizeChild == "boolean"){
27914             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27915         }else{
27916             this.resizeChild = Roo.get(this.resizeChild, true);
27917         }
27918     }
27919     
27920     if(this.adjustments == "auto"){
27921         var rc = this.resizeChild;
27922         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27923         if(rc && (hw || hn)){
27924             rc.position("relative");
27925             rc.setLeft(hw ? hw.el.getWidth() : 0);
27926             rc.setTop(hn ? hn.el.getHeight() : 0);
27927         }
27928         this.adjustments = [
27929             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27930             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27931         ];
27932     }
27933
27934     if(this.draggable){
27935         this.dd = this.dynamic ?
27936             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27937         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27938     }
27939
27940     // public events
27941     this.addEvents({
27942         /**
27943          * @event beforeresize
27944          * Fired before resize is allowed. Set enabled to false to cancel resize.
27945          * @param {Roo.Resizable} this
27946          * @param {Roo.EventObject} e The mousedown event
27947          */
27948         "beforeresize" : true,
27949         /**
27950          * @event resize
27951          * Fired after a resize.
27952          * @param {Roo.Resizable} this
27953          * @param {Number} width The new width
27954          * @param {Number} height The new height
27955          * @param {Roo.EventObject} e The mouseup event
27956          */
27957         "resize" : true
27958     });
27959
27960     if(this.width !== null && this.height !== null){
27961         this.resizeTo(this.width, this.height);
27962     }else{
27963         this.updateChildSize();
27964     }
27965     if(Roo.isIE){
27966         this.el.dom.style.zoom = 1;
27967     }
27968     Roo.Resizable.superclass.constructor.call(this);
27969 };
27970
27971 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27972         resizeChild : false,
27973         adjustments : [0, 0],
27974         minWidth : 5,
27975         minHeight : 5,
27976         maxWidth : 10000,
27977         maxHeight : 10000,
27978         enabled : true,
27979         animate : false,
27980         duration : .35,
27981         dynamic : false,
27982         handles : false,
27983         multiDirectional : false,
27984         disableTrackOver : false,
27985         easing : 'easeOutStrong',
27986         widthIncrement : 0,
27987         heightIncrement : 0,
27988         pinned : false,
27989         width : null,
27990         height : null,
27991         preserveRatio : false,
27992         transparent: false,
27993         minX: 0,
27994         minY: 0,
27995         draggable: false,
27996
27997         /**
27998          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27999          */
28000         constrainTo: undefined,
28001         /**
28002          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28003          */
28004         resizeRegion: undefined,
28005
28006
28007     /**
28008      * Perform a manual resize
28009      * @param {Number} width
28010      * @param {Number} height
28011      */
28012     resizeTo : function(width, height){
28013         this.el.setSize(width, height);
28014         this.updateChildSize();
28015         this.fireEvent("resize", this, width, height, null);
28016     },
28017
28018     // private
28019     startSizing : function(e, handle){
28020         this.fireEvent("beforeresize", this, e);
28021         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28022
28023             if(!this.overlay){
28024                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28025                 this.overlay.unselectable();
28026                 this.overlay.enableDisplayMode("block");
28027                 this.overlay.on("mousemove", this.onMouseMove, this);
28028                 this.overlay.on("mouseup", this.onMouseUp, this);
28029             }
28030             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28031
28032             this.resizing = true;
28033             this.startBox = this.el.getBox();
28034             this.startPoint = e.getXY();
28035             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28036                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28037
28038             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28039             this.overlay.show();
28040
28041             if(this.constrainTo) {
28042                 var ct = Roo.get(this.constrainTo);
28043                 this.resizeRegion = ct.getRegion().adjust(
28044                     ct.getFrameWidth('t'),
28045                     ct.getFrameWidth('l'),
28046                     -ct.getFrameWidth('b'),
28047                     -ct.getFrameWidth('r')
28048                 );
28049             }
28050
28051             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28052             this.proxy.show();
28053             this.proxy.setBox(this.startBox);
28054             if(!this.dynamic){
28055                 this.proxy.setStyle('visibility', 'visible');
28056             }
28057         }
28058     },
28059
28060     // private
28061     onMouseDown : function(handle, e){
28062         if(this.enabled){
28063             e.stopEvent();
28064             this.activeHandle = handle;
28065             this.startSizing(e, handle);
28066         }
28067     },
28068
28069     // private
28070     onMouseUp : function(e){
28071         var size = this.resizeElement();
28072         this.resizing = false;
28073         this.handleOut();
28074         this.overlay.hide();
28075         this.proxy.hide();
28076         this.fireEvent("resize", this, size.width, size.height, e);
28077     },
28078
28079     // private
28080     updateChildSize : function(){
28081         if(this.resizeChild){
28082             var el = this.el;
28083             var child = this.resizeChild;
28084             var adj = this.adjustments;
28085             if(el.dom.offsetWidth){
28086                 var b = el.getSize(true);
28087                 child.setSize(b.width+adj[0], b.height+adj[1]);
28088             }
28089             // Second call here for IE
28090             // The first call enables instant resizing and
28091             // the second call corrects scroll bars if they
28092             // exist
28093             if(Roo.isIE){
28094                 setTimeout(function(){
28095                     if(el.dom.offsetWidth){
28096                         var b = el.getSize(true);
28097                         child.setSize(b.width+adj[0], b.height+adj[1]);
28098                     }
28099                 }, 10);
28100             }
28101         }
28102     },
28103
28104     // private
28105     snap : function(value, inc, min){
28106         if(!inc || !value) return value;
28107         var newValue = value;
28108         var m = value % inc;
28109         if(m > 0){
28110             if(m > (inc/2)){
28111                 newValue = value + (inc-m);
28112             }else{
28113                 newValue = value - m;
28114             }
28115         }
28116         return Math.max(min, newValue);
28117     },
28118
28119     // private
28120     resizeElement : function(){
28121         var box = this.proxy.getBox();
28122         if(this.updateBox){
28123             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28124         }else{
28125             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28126         }
28127         this.updateChildSize();
28128         if(!this.dynamic){
28129             this.proxy.hide();
28130         }
28131         return box;
28132     },
28133
28134     // private
28135     constrain : function(v, diff, m, mx){
28136         if(v - diff < m){
28137             diff = v - m;
28138         }else if(v - diff > mx){
28139             diff = mx - v;
28140         }
28141         return diff;
28142     },
28143
28144     // private
28145     onMouseMove : function(e){
28146         if(this.enabled){
28147             try{// try catch so if something goes wrong the user doesn't get hung
28148
28149             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28150                 return;
28151             }
28152
28153             //var curXY = this.startPoint;
28154             var curSize = this.curSize || this.startBox;
28155             var x = this.startBox.x, y = this.startBox.y;
28156             var ox = x, oy = y;
28157             var w = curSize.width, h = curSize.height;
28158             var ow = w, oh = h;
28159             var mw = this.minWidth, mh = this.minHeight;
28160             var mxw = this.maxWidth, mxh = this.maxHeight;
28161             var wi = this.widthIncrement;
28162             var hi = this.heightIncrement;
28163
28164             var eventXY = e.getXY();
28165             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28166             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28167
28168             var pos = this.activeHandle.position;
28169
28170             switch(pos){
28171                 case "east":
28172                     w += diffX;
28173                     w = Math.min(Math.max(mw, w), mxw);
28174                     break;
28175              
28176                 case "south":
28177                     h += diffY;
28178                     h = Math.min(Math.max(mh, h), mxh);
28179                     break;
28180                 case "southeast":
28181                     w += diffX;
28182                     h += diffY;
28183                     w = Math.min(Math.max(mw, w), mxw);
28184                     h = Math.min(Math.max(mh, h), mxh);
28185                     break;
28186                 case "north":
28187                     diffY = this.constrain(h, diffY, mh, mxh);
28188                     y += diffY;
28189                     h -= diffY;
28190                     break;
28191                 case "hdrag":
28192                     
28193                     if (wi) {
28194                         var adiffX = Math.abs(diffX);
28195                         var sub = (adiffX % wi); // how much 
28196                         if (sub > (wi/2)) { // far enough to snap
28197                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28198                         } else {
28199                             // remove difference.. 
28200                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28201                         }
28202                     }
28203                     x += diffX;
28204                     x = Math.max(this.minX, x);
28205                     break;
28206                 case "west":
28207                     diffX = this.constrain(w, diffX, mw, mxw);
28208                     x += diffX;
28209                     w -= diffX;
28210                     break;
28211                 case "northeast":
28212                     w += diffX;
28213                     w = Math.min(Math.max(mw, w), mxw);
28214                     diffY = this.constrain(h, diffY, mh, mxh);
28215                     y += diffY;
28216                     h -= diffY;
28217                     break;
28218                 case "northwest":
28219                     diffX = this.constrain(w, diffX, mw, mxw);
28220                     diffY = this.constrain(h, diffY, mh, mxh);
28221                     y += diffY;
28222                     h -= diffY;
28223                     x += diffX;
28224                     w -= diffX;
28225                     break;
28226                case "southwest":
28227                     diffX = this.constrain(w, diffX, mw, mxw);
28228                     h += diffY;
28229                     h = Math.min(Math.max(mh, h), mxh);
28230                     x += diffX;
28231                     w -= diffX;
28232                     break;
28233             }
28234
28235             var sw = this.snap(w, wi, mw);
28236             var sh = this.snap(h, hi, mh);
28237             if(sw != w || sh != h){
28238                 switch(pos){
28239                     case "northeast":
28240                         y -= sh - h;
28241                     break;
28242                     case "north":
28243                         y -= sh - h;
28244                         break;
28245                     case "southwest":
28246                         x -= sw - w;
28247                     break;
28248                     case "west":
28249                         x -= sw - w;
28250                         break;
28251                     case "northwest":
28252                         x -= sw - w;
28253                         y -= sh - h;
28254                     break;
28255                 }
28256                 w = sw;
28257                 h = sh;
28258             }
28259
28260             if(this.preserveRatio){
28261                 switch(pos){
28262                     case "southeast":
28263                     case "east":
28264                         h = oh * (w/ow);
28265                         h = Math.min(Math.max(mh, h), mxh);
28266                         w = ow * (h/oh);
28267                        break;
28268                     case "south":
28269                         w = ow * (h/oh);
28270                         w = Math.min(Math.max(mw, w), mxw);
28271                         h = oh * (w/ow);
28272                         break;
28273                     case "northeast":
28274                         w = ow * (h/oh);
28275                         w = Math.min(Math.max(mw, w), mxw);
28276                         h = oh * (w/ow);
28277                     break;
28278                     case "north":
28279                         var tw = w;
28280                         w = ow * (h/oh);
28281                         w = Math.min(Math.max(mw, w), mxw);
28282                         h = oh * (w/ow);
28283                         x += (tw - w) / 2;
28284                         break;
28285                     case "southwest":
28286                         h = oh * (w/ow);
28287                         h = Math.min(Math.max(mh, h), mxh);
28288                         var tw = w;
28289                         w = ow * (h/oh);
28290                         x += tw - w;
28291                         break;
28292                     case "west":
28293                         var th = h;
28294                         h = oh * (w/ow);
28295                         h = Math.min(Math.max(mh, h), mxh);
28296                         y += (th - h) / 2;
28297                         var tw = w;
28298                         w = ow * (h/oh);
28299                         x += tw - w;
28300                        break;
28301                     case "northwest":
28302                         var tw = w;
28303                         var th = h;
28304                         h = oh * (w/ow);
28305                         h = Math.min(Math.max(mh, h), mxh);
28306                         w = ow * (h/oh);
28307                         y += th - h;
28308                         x += tw - w;
28309                        break;
28310
28311                 }
28312             }
28313             if (pos == 'hdrag') {
28314                 w = ow;
28315             }
28316             this.proxy.setBounds(x, y, w, h);
28317             if(this.dynamic){
28318                 this.resizeElement();
28319             }
28320             }catch(e){}
28321         }
28322     },
28323
28324     // private
28325     handleOver : function(){
28326         if(this.enabled){
28327             this.el.addClass("x-resizable-over");
28328         }
28329     },
28330
28331     // private
28332     handleOut : function(){
28333         if(!this.resizing){
28334             this.el.removeClass("x-resizable-over");
28335         }
28336     },
28337
28338     /**
28339      * Returns the element this component is bound to.
28340      * @return {Roo.Element}
28341      */
28342     getEl : function(){
28343         return this.el;
28344     },
28345
28346     /**
28347      * Returns the resizeChild element (or null).
28348      * @return {Roo.Element}
28349      */
28350     getResizeChild : function(){
28351         return this.resizeChild;
28352     },
28353
28354     /**
28355      * Destroys this resizable. If the element was wrapped and
28356      * removeEl is not true then the element remains.
28357      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28358      */
28359     destroy : function(removeEl){
28360         this.proxy.remove();
28361         if(this.overlay){
28362             this.overlay.removeAllListeners();
28363             this.overlay.remove();
28364         }
28365         var ps = Roo.Resizable.positions;
28366         for(var k in ps){
28367             if(typeof ps[k] != "function" && this[ps[k]]){
28368                 var h = this[ps[k]];
28369                 h.el.removeAllListeners();
28370                 h.el.remove();
28371             }
28372         }
28373         if(removeEl){
28374             this.el.update("");
28375             this.el.remove();
28376         }
28377     }
28378 });
28379
28380 // private
28381 // hash to map config positions to true positions
28382 Roo.Resizable.positions = {
28383     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28384     hd: "hdrag"
28385 };
28386
28387 // private
28388 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28389     if(!this.tpl){
28390         // only initialize the template if resizable is used
28391         var tpl = Roo.DomHelper.createTemplate(
28392             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28393         );
28394         tpl.compile();
28395         Roo.Resizable.Handle.prototype.tpl = tpl;
28396     }
28397     this.position = pos;
28398     this.rz = rz;
28399     // show north drag fro topdra
28400     var handlepos = pos == 'hdrag' ? 'north' : pos;
28401     
28402     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28403     if (pos == 'hdrag') {
28404         this.el.setStyle('cursor', 'pointer');
28405     }
28406     this.el.unselectable();
28407     if(transparent){
28408         this.el.setOpacity(0);
28409     }
28410     this.el.on("mousedown", this.onMouseDown, this);
28411     if(!disableTrackOver){
28412         this.el.on("mouseover", this.onMouseOver, this);
28413         this.el.on("mouseout", this.onMouseOut, this);
28414     }
28415 };
28416
28417 // private
28418 Roo.Resizable.Handle.prototype = {
28419     afterResize : function(rz){
28420         // do nothing
28421     },
28422     // private
28423     onMouseDown : function(e){
28424         this.rz.onMouseDown(this, e);
28425     },
28426     // private
28427     onMouseOver : function(e){
28428         this.rz.handleOver(this, e);
28429     },
28430     // private
28431     onMouseOut : function(e){
28432         this.rz.handleOut(this, e);
28433     }
28434 };/*
28435  * Based on:
28436  * Ext JS Library 1.1.1
28437  * Copyright(c) 2006-2007, Ext JS, LLC.
28438  *
28439  * Originally Released Under LGPL - original licence link has changed is not relivant.
28440  *
28441  * Fork - LGPL
28442  * <script type="text/javascript">
28443  */
28444
28445 /**
28446  * @class Roo.Editor
28447  * @extends Roo.Component
28448  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28449  * @constructor
28450  * Create a new Editor
28451  * @param {Roo.form.Field} field The Field object (or descendant)
28452  * @param {Object} config The config object
28453  */
28454 Roo.Editor = function(field, config){
28455     Roo.Editor.superclass.constructor.call(this, config);
28456     this.field = field;
28457     this.addEvents({
28458         /**
28459              * @event beforestartedit
28460              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28461              * false from the handler of this event.
28462              * @param {Editor} this
28463              * @param {Roo.Element} boundEl The underlying element bound to this editor
28464              * @param {Mixed} value The field value being set
28465              */
28466         "beforestartedit" : true,
28467         /**
28468              * @event startedit
28469              * Fires when this editor is displayed
28470              * @param {Roo.Element} boundEl The underlying element bound to this editor
28471              * @param {Mixed} value The starting field value
28472              */
28473         "startedit" : true,
28474         /**
28475              * @event beforecomplete
28476              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28477              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28478              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28479              * event will not fire since no edit actually occurred.
28480              * @param {Editor} this
28481              * @param {Mixed} value The current field value
28482              * @param {Mixed} startValue The original field value
28483              */
28484         "beforecomplete" : true,
28485         /**
28486              * @event complete
28487              * Fires after editing is complete and any changed value has been written to the underlying field.
28488              * @param {Editor} this
28489              * @param {Mixed} value The current field value
28490              * @param {Mixed} startValue The original field value
28491              */
28492         "complete" : true,
28493         /**
28494          * @event specialkey
28495          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28496          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28497          * @param {Roo.form.Field} this
28498          * @param {Roo.EventObject} e The event object
28499          */
28500         "specialkey" : true
28501     });
28502 };
28503
28504 Roo.extend(Roo.Editor, Roo.Component, {
28505     /**
28506      * @cfg {Boolean/String} autosize
28507      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28508      * or "height" to adopt the height only (defaults to false)
28509      */
28510     /**
28511      * @cfg {Boolean} revertInvalid
28512      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28513      * validation fails (defaults to true)
28514      */
28515     /**
28516      * @cfg {Boolean} ignoreNoChange
28517      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28518      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28519      * will never be ignored.
28520      */
28521     /**
28522      * @cfg {Boolean} hideEl
28523      * False to keep the bound element visible while the editor is displayed (defaults to true)
28524      */
28525     /**
28526      * @cfg {Mixed} value
28527      * The data value of the underlying field (defaults to "")
28528      */
28529     value : "",
28530     /**
28531      * @cfg {String} alignment
28532      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28533      */
28534     alignment: "c-c?",
28535     /**
28536      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28537      * for bottom-right shadow (defaults to "frame")
28538      */
28539     shadow : "frame",
28540     /**
28541      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28542      */
28543     constrain : false,
28544     /**
28545      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28546      */
28547     completeOnEnter : false,
28548     /**
28549      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28550      */
28551     cancelOnEsc : false,
28552     /**
28553      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28554      */
28555     updateEl : false,
28556
28557     // private
28558     onRender : function(ct, position){
28559         this.el = new Roo.Layer({
28560             shadow: this.shadow,
28561             cls: "x-editor",
28562             parentEl : ct,
28563             shim : this.shim,
28564             shadowOffset:4,
28565             id: this.id,
28566             constrain: this.constrain
28567         });
28568         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28569         if(this.field.msgTarget != 'title'){
28570             this.field.msgTarget = 'qtip';
28571         }
28572         this.field.render(this.el);
28573         if(Roo.isGecko){
28574             this.field.el.dom.setAttribute('autocomplete', 'off');
28575         }
28576         this.field.on("specialkey", this.onSpecialKey, this);
28577         if(this.swallowKeys){
28578             this.field.el.swallowEvent(['keydown','keypress']);
28579         }
28580         this.field.show();
28581         this.field.on("blur", this.onBlur, this);
28582         if(this.field.grow){
28583             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28584         }
28585     },
28586
28587     onSpecialKey : function(field, e)
28588     {
28589         //Roo.log('editor onSpecialKey');
28590         if(this.completeOnEnter && e.getKey() == e.ENTER){
28591             e.stopEvent();
28592             this.completeEdit();
28593             return;
28594         }
28595         // do not fire special key otherwise it might hide close the editor...
28596         if(e.getKey() == e.ENTER){    
28597             return;
28598         }
28599         if(this.cancelOnEsc && e.getKey() == e.ESC){
28600             this.cancelEdit();
28601             return;
28602         } 
28603         this.fireEvent('specialkey', field, e);
28604     
28605     },
28606
28607     /**
28608      * Starts the editing process and shows the editor.
28609      * @param {String/HTMLElement/Element} el The element to edit
28610      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28611       * to the innerHTML of el.
28612      */
28613     startEdit : function(el, value){
28614         if(this.editing){
28615             this.completeEdit();
28616         }
28617         this.boundEl = Roo.get(el);
28618         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28619         if(!this.rendered){
28620             this.render(this.parentEl || document.body);
28621         }
28622         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28623             return;
28624         }
28625         this.startValue = v;
28626         this.field.setValue(v);
28627         if(this.autoSize){
28628             var sz = this.boundEl.getSize();
28629             switch(this.autoSize){
28630                 case "width":
28631                 this.setSize(sz.width,  "");
28632                 break;
28633                 case "height":
28634                 this.setSize("",  sz.height);
28635                 break;
28636                 default:
28637                 this.setSize(sz.width,  sz.height);
28638             }
28639         }
28640         this.el.alignTo(this.boundEl, this.alignment);
28641         this.editing = true;
28642         if(Roo.QuickTips){
28643             Roo.QuickTips.disable();
28644         }
28645         this.show();
28646     },
28647
28648     /**
28649      * Sets the height and width of this editor.
28650      * @param {Number} width The new width
28651      * @param {Number} height The new height
28652      */
28653     setSize : function(w, h){
28654         this.field.setSize(w, h);
28655         if(this.el){
28656             this.el.sync();
28657         }
28658     },
28659
28660     /**
28661      * Realigns the editor to the bound field based on the current alignment config value.
28662      */
28663     realign : function(){
28664         this.el.alignTo(this.boundEl, this.alignment);
28665     },
28666
28667     /**
28668      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28669      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28670      */
28671     completeEdit : function(remainVisible){
28672         if(!this.editing){
28673             return;
28674         }
28675         var v = this.getValue();
28676         if(this.revertInvalid !== false && !this.field.isValid()){
28677             v = this.startValue;
28678             this.cancelEdit(true);
28679         }
28680         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28681             this.editing = false;
28682             this.hide();
28683             return;
28684         }
28685         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28686             this.editing = false;
28687             if(this.updateEl && this.boundEl){
28688                 this.boundEl.update(v);
28689             }
28690             if(remainVisible !== true){
28691                 this.hide();
28692             }
28693             this.fireEvent("complete", this, v, this.startValue);
28694         }
28695     },
28696
28697     // private
28698     onShow : function(){
28699         this.el.show();
28700         if(this.hideEl !== false){
28701             this.boundEl.hide();
28702         }
28703         this.field.show();
28704         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28705             this.fixIEFocus = true;
28706             this.deferredFocus.defer(50, this);
28707         }else{
28708             this.field.focus();
28709         }
28710         this.fireEvent("startedit", this.boundEl, this.startValue);
28711     },
28712
28713     deferredFocus : function(){
28714         if(this.editing){
28715             this.field.focus();
28716         }
28717     },
28718
28719     /**
28720      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28721      * reverted to the original starting value.
28722      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28723      * cancel (defaults to false)
28724      */
28725     cancelEdit : function(remainVisible){
28726         if(this.editing){
28727             this.setValue(this.startValue);
28728             if(remainVisible !== true){
28729                 this.hide();
28730             }
28731         }
28732     },
28733
28734     // private
28735     onBlur : function(){
28736         if(this.allowBlur !== true && this.editing){
28737             this.completeEdit();
28738         }
28739     },
28740
28741     // private
28742     onHide : function(){
28743         if(this.editing){
28744             this.completeEdit();
28745             return;
28746         }
28747         this.field.blur();
28748         if(this.field.collapse){
28749             this.field.collapse();
28750         }
28751         this.el.hide();
28752         if(this.hideEl !== false){
28753             this.boundEl.show();
28754         }
28755         if(Roo.QuickTips){
28756             Roo.QuickTips.enable();
28757         }
28758     },
28759
28760     /**
28761      * Sets the data value of the editor
28762      * @param {Mixed} value Any valid value supported by the underlying field
28763      */
28764     setValue : function(v){
28765         this.field.setValue(v);
28766     },
28767
28768     /**
28769      * Gets the data value of the editor
28770      * @return {Mixed} The data value
28771      */
28772     getValue : function(){
28773         return this.field.getValue();
28774     }
28775 });/*
28776  * Based on:
28777  * Ext JS Library 1.1.1
28778  * Copyright(c) 2006-2007, Ext JS, LLC.
28779  *
28780  * Originally Released Under LGPL - original licence link has changed is not relivant.
28781  *
28782  * Fork - LGPL
28783  * <script type="text/javascript">
28784  */
28785  
28786 /**
28787  * @class Roo.BasicDialog
28788  * @extends Roo.util.Observable
28789  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28790  * <pre><code>
28791 var dlg = new Roo.BasicDialog("my-dlg", {
28792     height: 200,
28793     width: 300,
28794     minHeight: 100,
28795     minWidth: 150,
28796     modal: true,
28797     proxyDrag: true,
28798     shadow: true
28799 });
28800 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28801 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28802 dlg.addButton('Cancel', dlg.hide, dlg);
28803 dlg.show();
28804 </code></pre>
28805   <b>A Dialog should always be a direct child of the body element.</b>
28806  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28807  * @cfg {String} title Default text to display in the title bar (defaults to null)
28808  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28809  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28810  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28811  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28812  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28813  * (defaults to null with no animation)
28814  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28815  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28816  * property for valid values (defaults to 'all')
28817  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28818  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28819  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28820  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28821  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28822  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28823  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28824  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28825  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28826  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28827  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28828  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28829  * draggable = true (defaults to false)
28830  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28831  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28832  * shadow (defaults to false)
28833  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28834  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28835  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28836  * @cfg {Array} buttons Array of buttons
28837  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28838  * @constructor
28839  * Create a new BasicDialog.
28840  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28841  * @param {Object} config Configuration options
28842  */
28843 Roo.BasicDialog = function(el, config){
28844     this.el = Roo.get(el);
28845     var dh = Roo.DomHelper;
28846     if(!this.el && config && config.autoCreate){
28847         if(typeof config.autoCreate == "object"){
28848             if(!config.autoCreate.id){
28849                 config.autoCreate.id = el;
28850             }
28851             this.el = dh.append(document.body,
28852                         config.autoCreate, true);
28853         }else{
28854             this.el = dh.append(document.body,
28855                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28856         }
28857     }
28858     el = this.el;
28859     el.setDisplayed(true);
28860     el.hide = this.hideAction;
28861     this.id = el.id;
28862     el.addClass("x-dlg");
28863
28864     Roo.apply(this, config);
28865
28866     this.proxy = el.createProxy("x-dlg-proxy");
28867     this.proxy.hide = this.hideAction;
28868     this.proxy.setOpacity(.5);
28869     this.proxy.hide();
28870
28871     if(config.width){
28872         el.setWidth(config.width);
28873     }
28874     if(config.height){
28875         el.setHeight(config.height);
28876     }
28877     this.size = el.getSize();
28878     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28879         this.xy = [config.x,config.y];
28880     }else{
28881         this.xy = el.getCenterXY(true);
28882     }
28883     /** The header element @type Roo.Element */
28884     this.header = el.child("> .x-dlg-hd");
28885     /** The body element @type Roo.Element */
28886     this.body = el.child("> .x-dlg-bd");
28887     /** The footer element @type Roo.Element */
28888     this.footer = el.child("> .x-dlg-ft");
28889
28890     if(!this.header){
28891         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28892     }
28893     if(!this.body){
28894         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28895     }
28896
28897     this.header.unselectable();
28898     if(this.title){
28899         this.header.update(this.title);
28900     }
28901     // this element allows the dialog to be focused for keyboard event
28902     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28903     this.focusEl.swallowEvent("click", true);
28904
28905     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28906
28907     // wrap the body and footer for special rendering
28908     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28909     if(this.footer){
28910         this.bwrap.dom.appendChild(this.footer.dom);
28911     }
28912
28913     this.bg = this.el.createChild({
28914         tag: "div", cls:"x-dlg-bg",
28915         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28916     });
28917     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28918
28919
28920     if(this.autoScroll !== false && !this.autoTabs){
28921         this.body.setStyle("overflow", "auto");
28922     }
28923
28924     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28925
28926     if(this.closable !== false){
28927         this.el.addClass("x-dlg-closable");
28928         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28929         this.close.on("click", this.closeClick, this);
28930         this.close.addClassOnOver("x-dlg-close-over");
28931     }
28932     if(this.collapsible !== false){
28933         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28934         this.collapseBtn.on("click", this.collapseClick, this);
28935         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28936         this.header.on("dblclick", this.collapseClick, this);
28937     }
28938     if(this.resizable !== false){
28939         this.el.addClass("x-dlg-resizable");
28940         this.resizer = new Roo.Resizable(el, {
28941             minWidth: this.minWidth || 80,
28942             minHeight:this.minHeight || 80,
28943             handles: this.resizeHandles || "all",
28944             pinned: true
28945         });
28946         this.resizer.on("beforeresize", this.beforeResize, this);
28947         this.resizer.on("resize", this.onResize, this);
28948     }
28949     if(this.draggable !== false){
28950         el.addClass("x-dlg-draggable");
28951         if (!this.proxyDrag) {
28952             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28953         }
28954         else {
28955             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28956         }
28957         dd.setHandleElId(this.header.id);
28958         dd.endDrag = this.endMove.createDelegate(this);
28959         dd.startDrag = this.startMove.createDelegate(this);
28960         dd.onDrag = this.onDrag.createDelegate(this);
28961         dd.scroll = false;
28962         this.dd = dd;
28963     }
28964     if(this.modal){
28965         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28966         this.mask.enableDisplayMode("block");
28967         this.mask.hide();
28968         this.el.addClass("x-dlg-modal");
28969     }
28970     if(this.shadow){
28971         this.shadow = new Roo.Shadow({
28972             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28973             offset : this.shadowOffset
28974         });
28975     }else{
28976         this.shadowOffset = 0;
28977     }
28978     if(Roo.useShims && this.shim !== false){
28979         this.shim = this.el.createShim();
28980         this.shim.hide = this.hideAction;
28981         this.shim.hide();
28982     }else{
28983         this.shim = false;
28984     }
28985     if(this.autoTabs){
28986         this.initTabs();
28987     }
28988     if (this.buttons) { 
28989         var bts= this.buttons;
28990         this.buttons = [];
28991         Roo.each(bts, function(b) {
28992             this.addButton(b);
28993         }, this);
28994     }
28995     
28996     
28997     this.addEvents({
28998         /**
28999          * @event keydown
29000          * Fires when a key is pressed
29001          * @param {Roo.BasicDialog} this
29002          * @param {Roo.EventObject} e
29003          */
29004         "keydown" : true,
29005         /**
29006          * @event move
29007          * Fires when this dialog is moved by the user.
29008          * @param {Roo.BasicDialog} this
29009          * @param {Number} x The new page X
29010          * @param {Number} y The new page Y
29011          */
29012         "move" : true,
29013         /**
29014          * @event resize
29015          * Fires when this dialog is resized by the user.
29016          * @param {Roo.BasicDialog} this
29017          * @param {Number} width The new width
29018          * @param {Number} height The new height
29019          */
29020         "resize" : true,
29021         /**
29022          * @event beforehide
29023          * Fires before this dialog is hidden.
29024          * @param {Roo.BasicDialog} this
29025          */
29026         "beforehide" : true,
29027         /**
29028          * @event hide
29029          * Fires when this dialog is hidden.
29030          * @param {Roo.BasicDialog} this
29031          */
29032         "hide" : true,
29033         /**
29034          * @event beforeshow
29035          * Fires before this dialog is shown.
29036          * @param {Roo.BasicDialog} this
29037          */
29038         "beforeshow" : true,
29039         /**
29040          * @event show
29041          * Fires when this dialog is shown.
29042          * @param {Roo.BasicDialog} this
29043          */
29044         "show" : true
29045     });
29046     el.on("keydown", this.onKeyDown, this);
29047     el.on("mousedown", this.toFront, this);
29048     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29049     this.el.hide();
29050     Roo.DialogManager.register(this);
29051     Roo.BasicDialog.superclass.constructor.call(this);
29052 };
29053
29054 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29055     shadowOffset: Roo.isIE ? 6 : 5,
29056     minHeight: 80,
29057     minWidth: 200,
29058     minButtonWidth: 75,
29059     defaultButton: null,
29060     buttonAlign: "right",
29061     tabTag: 'div',
29062     firstShow: true,
29063
29064     /**
29065      * Sets the dialog title text
29066      * @param {String} text The title text to display
29067      * @return {Roo.BasicDialog} this
29068      */
29069     setTitle : function(text){
29070         this.header.update(text);
29071         return this;
29072     },
29073
29074     // private
29075     closeClick : function(){
29076         this.hide();
29077     },
29078
29079     // private
29080     collapseClick : function(){
29081         this[this.collapsed ? "expand" : "collapse"]();
29082     },
29083
29084     /**
29085      * Collapses the dialog to its minimized state (only the title bar is visible).
29086      * Equivalent to the user clicking the collapse dialog button.
29087      */
29088     collapse : function(){
29089         if(!this.collapsed){
29090             this.collapsed = true;
29091             this.el.addClass("x-dlg-collapsed");
29092             this.restoreHeight = this.el.getHeight();
29093             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29094         }
29095     },
29096
29097     /**
29098      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29099      * clicking the expand dialog button.
29100      */
29101     expand : function(){
29102         if(this.collapsed){
29103             this.collapsed = false;
29104             this.el.removeClass("x-dlg-collapsed");
29105             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29106         }
29107     },
29108
29109     /**
29110      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29111      * @return {Roo.TabPanel} The tabs component
29112      */
29113     initTabs : function(){
29114         var tabs = this.getTabs();
29115         while(tabs.getTab(0)){
29116             tabs.removeTab(0);
29117         }
29118         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29119             var dom = el.dom;
29120             tabs.addTab(Roo.id(dom), dom.title);
29121             dom.title = "";
29122         });
29123         tabs.activate(0);
29124         return tabs;
29125     },
29126
29127     // private
29128     beforeResize : function(){
29129         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29130     },
29131
29132     // private
29133     onResize : function(){
29134         this.refreshSize();
29135         this.syncBodyHeight();
29136         this.adjustAssets();
29137         this.focus();
29138         this.fireEvent("resize", this, this.size.width, this.size.height);
29139     },
29140
29141     // private
29142     onKeyDown : function(e){
29143         if(this.isVisible()){
29144             this.fireEvent("keydown", this, e);
29145         }
29146     },
29147
29148     /**
29149      * Resizes the dialog.
29150      * @param {Number} width
29151      * @param {Number} height
29152      * @return {Roo.BasicDialog} this
29153      */
29154     resizeTo : function(width, height){
29155         this.el.setSize(width, height);
29156         this.size = {width: width, height: height};
29157         this.syncBodyHeight();
29158         if(this.fixedcenter){
29159             this.center();
29160         }
29161         if(this.isVisible()){
29162             this.constrainXY();
29163             this.adjustAssets();
29164         }
29165         this.fireEvent("resize", this, width, height);
29166         return this;
29167     },
29168
29169
29170     /**
29171      * Resizes the dialog to fit the specified content size.
29172      * @param {Number} width
29173      * @param {Number} height
29174      * @return {Roo.BasicDialog} this
29175      */
29176     setContentSize : function(w, h){
29177         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29178         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29179         //if(!this.el.isBorderBox()){
29180             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29181             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29182         //}
29183         if(this.tabs){
29184             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29185             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29186         }
29187         this.resizeTo(w, h);
29188         return this;
29189     },
29190
29191     /**
29192      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29193      * executed in response to a particular key being pressed while the dialog is active.
29194      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29195      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29196      * @param {Function} fn The function to call
29197      * @param {Object} scope (optional) The scope of the function
29198      * @return {Roo.BasicDialog} this
29199      */
29200     addKeyListener : function(key, fn, scope){
29201         var keyCode, shift, ctrl, alt;
29202         if(typeof key == "object" && !(key instanceof Array)){
29203             keyCode = key["key"];
29204             shift = key["shift"];
29205             ctrl = key["ctrl"];
29206             alt = key["alt"];
29207         }else{
29208             keyCode = key;
29209         }
29210         var handler = function(dlg, e){
29211             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29212                 var k = e.getKey();
29213                 if(keyCode instanceof Array){
29214                     for(var i = 0, len = keyCode.length; i < len; i++){
29215                         if(keyCode[i] == k){
29216                           fn.call(scope || window, dlg, k, e);
29217                           return;
29218                         }
29219                     }
29220                 }else{
29221                     if(k == keyCode){
29222                         fn.call(scope || window, dlg, k, e);
29223                     }
29224                 }
29225             }
29226         };
29227         this.on("keydown", handler);
29228         return this;
29229     },
29230
29231     /**
29232      * Returns the TabPanel component (creates it if it doesn't exist).
29233      * Note: If you wish to simply check for the existence of tabs without creating them,
29234      * check for a null 'tabs' property.
29235      * @return {Roo.TabPanel} The tabs component
29236      */
29237     getTabs : function(){
29238         if(!this.tabs){
29239             this.el.addClass("x-dlg-auto-tabs");
29240             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29241             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29242         }
29243         return this.tabs;
29244     },
29245
29246     /**
29247      * Adds a button to the footer section of the dialog.
29248      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29249      * object or a valid Roo.DomHelper element config
29250      * @param {Function} handler The function called when the button is clicked
29251      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29252      * @return {Roo.Button} The new button
29253      */
29254     addButton : function(config, handler, scope){
29255         var dh = Roo.DomHelper;
29256         if(!this.footer){
29257             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29258         }
29259         if(!this.btnContainer){
29260             var tb = this.footer.createChild({
29261
29262                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29263                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29264             }, null, true);
29265             this.btnContainer = tb.firstChild.firstChild.firstChild;
29266         }
29267         var bconfig = {
29268             handler: handler,
29269             scope: scope,
29270             minWidth: this.minButtonWidth,
29271             hideParent:true
29272         };
29273         if(typeof config == "string"){
29274             bconfig.text = config;
29275         }else{
29276             if(config.tag){
29277                 bconfig.dhconfig = config;
29278             }else{
29279                 Roo.apply(bconfig, config);
29280             }
29281         }
29282         var fc = false;
29283         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29284             bconfig.position = Math.max(0, bconfig.position);
29285             fc = this.btnContainer.childNodes[bconfig.position];
29286         }
29287          
29288         var btn = new Roo.Button(
29289             fc ? 
29290                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29291                 : this.btnContainer.appendChild(document.createElement("td")),
29292             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29293             bconfig
29294         );
29295         this.syncBodyHeight();
29296         if(!this.buttons){
29297             /**
29298              * Array of all the buttons that have been added to this dialog via addButton
29299              * @type Array
29300              */
29301             this.buttons = [];
29302         }
29303         this.buttons.push(btn);
29304         return btn;
29305     },
29306
29307     /**
29308      * Sets the default button to be focused when the dialog is displayed.
29309      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29310      * @return {Roo.BasicDialog} this
29311      */
29312     setDefaultButton : function(btn){
29313         this.defaultButton = btn;
29314         return this;
29315     },
29316
29317     // private
29318     getHeaderFooterHeight : function(safe){
29319         var height = 0;
29320         if(this.header){
29321            height += this.header.getHeight();
29322         }
29323         if(this.footer){
29324            var fm = this.footer.getMargins();
29325             height += (this.footer.getHeight()+fm.top+fm.bottom);
29326         }
29327         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29328         height += this.centerBg.getPadding("tb");
29329         return height;
29330     },
29331
29332     // private
29333     syncBodyHeight : function(){
29334         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29335         var height = this.size.height - this.getHeaderFooterHeight(false);
29336         bd.setHeight(height-bd.getMargins("tb"));
29337         var hh = this.header.getHeight();
29338         var h = this.size.height-hh;
29339         cb.setHeight(h);
29340         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29341         bw.setHeight(h-cb.getPadding("tb"));
29342         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29343         bd.setWidth(bw.getWidth(true));
29344         if(this.tabs){
29345             this.tabs.syncHeight();
29346             if(Roo.isIE){
29347                 this.tabs.el.repaint();
29348             }
29349         }
29350     },
29351
29352     /**
29353      * Restores the previous state of the dialog if Roo.state is configured.
29354      * @return {Roo.BasicDialog} this
29355      */
29356     restoreState : function(){
29357         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29358         if(box && box.width){
29359             this.xy = [box.x, box.y];
29360             this.resizeTo(box.width, box.height);
29361         }
29362         return this;
29363     },
29364
29365     // private
29366     beforeShow : function(){
29367         this.expand();
29368         if(this.fixedcenter){
29369             this.xy = this.el.getCenterXY(true);
29370         }
29371         if(this.modal){
29372             Roo.get(document.body).addClass("x-body-masked");
29373             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29374             this.mask.show();
29375         }
29376         this.constrainXY();
29377     },
29378
29379     // private
29380     animShow : function(){
29381         var b = Roo.get(this.animateTarget).getBox();
29382         this.proxy.setSize(b.width, b.height);
29383         this.proxy.setLocation(b.x, b.y);
29384         this.proxy.show();
29385         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29386                     true, .35, this.showEl.createDelegate(this));
29387     },
29388
29389     /**
29390      * Shows the dialog.
29391      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29392      * @return {Roo.BasicDialog} this
29393      */
29394     show : function(animateTarget){
29395         if (this.fireEvent("beforeshow", this) === false){
29396             return;
29397         }
29398         if(this.syncHeightBeforeShow){
29399             this.syncBodyHeight();
29400         }else if(this.firstShow){
29401             this.firstShow = false;
29402             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29403         }
29404         this.animateTarget = animateTarget || this.animateTarget;
29405         if(!this.el.isVisible()){
29406             this.beforeShow();
29407             if(this.animateTarget && Roo.get(this.animateTarget)){
29408                 this.animShow();
29409             }else{
29410                 this.showEl();
29411             }
29412         }
29413         return this;
29414     },
29415
29416     // private
29417     showEl : function(){
29418         this.proxy.hide();
29419         this.el.setXY(this.xy);
29420         this.el.show();
29421         this.adjustAssets(true);
29422         this.toFront();
29423         this.focus();
29424         // IE peekaboo bug - fix found by Dave Fenwick
29425         if(Roo.isIE){
29426             this.el.repaint();
29427         }
29428         this.fireEvent("show", this);
29429     },
29430
29431     /**
29432      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29433      * dialog itself will receive focus.
29434      */
29435     focus : function(){
29436         if(this.defaultButton){
29437             this.defaultButton.focus();
29438         }else{
29439             this.focusEl.focus();
29440         }
29441     },
29442
29443     // private
29444     constrainXY : function(){
29445         if(this.constraintoviewport !== false){
29446             if(!this.viewSize){
29447                 if(this.container){
29448                     var s = this.container.getSize();
29449                     this.viewSize = [s.width, s.height];
29450                 }else{
29451                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29452                 }
29453             }
29454             var s = Roo.get(this.container||document).getScroll();
29455
29456             var x = this.xy[0], y = this.xy[1];
29457             var w = this.size.width, h = this.size.height;
29458             var vw = this.viewSize[0], vh = this.viewSize[1];
29459             // only move it if it needs it
29460             var moved = false;
29461             // first validate right/bottom
29462             if(x + w > vw+s.left){
29463                 x = vw - w;
29464                 moved = true;
29465             }
29466             if(y + h > vh+s.top){
29467                 y = vh - h;
29468                 moved = true;
29469             }
29470             // then make sure top/left isn't negative
29471             if(x < s.left){
29472                 x = s.left;
29473                 moved = true;
29474             }
29475             if(y < s.top){
29476                 y = s.top;
29477                 moved = true;
29478             }
29479             if(moved){
29480                 // cache xy
29481                 this.xy = [x, y];
29482                 if(this.isVisible()){
29483                     this.el.setLocation(x, y);
29484                     this.adjustAssets();
29485                 }
29486             }
29487         }
29488     },
29489
29490     // private
29491     onDrag : function(){
29492         if(!this.proxyDrag){
29493             this.xy = this.el.getXY();
29494             this.adjustAssets();
29495         }
29496     },
29497
29498     // private
29499     adjustAssets : function(doShow){
29500         var x = this.xy[0], y = this.xy[1];
29501         var w = this.size.width, h = this.size.height;
29502         if(doShow === true){
29503             if(this.shadow){
29504                 this.shadow.show(this.el);
29505             }
29506             if(this.shim){
29507                 this.shim.show();
29508             }
29509         }
29510         if(this.shadow && this.shadow.isVisible()){
29511             this.shadow.show(this.el);
29512         }
29513         if(this.shim && this.shim.isVisible()){
29514             this.shim.setBounds(x, y, w, h);
29515         }
29516     },
29517
29518     // private
29519     adjustViewport : function(w, h){
29520         if(!w || !h){
29521             w = Roo.lib.Dom.getViewWidth();
29522             h = Roo.lib.Dom.getViewHeight();
29523         }
29524         // cache the size
29525         this.viewSize = [w, h];
29526         if(this.modal && this.mask.isVisible()){
29527             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29528             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29529         }
29530         if(this.isVisible()){
29531             this.constrainXY();
29532         }
29533     },
29534
29535     /**
29536      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29537      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29538      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29539      */
29540     destroy : function(removeEl){
29541         if(this.isVisible()){
29542             this.animateTarget = null;
29543             this.hide();
29544         }
29545         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29546         if(this.tabs){
29547             this.tabs.destroy(removeEl);
29548         }
29549         Roo.destroy(
29550              this.shim,
29551              this.proxy,
29552              this.resizer,
29553              this.close,
29554              this.mask
29555         );
29556         if(this.dd){
29557             this.dd.unreg();
29558         }
29559         if(this.buttons){
29560            for(var i = 0, len = this.buttons.length; i < len; i++){
29561                this.buttons[i].destroy();
29562            }
29563         }
29564         this.el.removeAllListeners();
29565         if(removeEl === true){
29566             this.el.update("");
29567             this.el.remove();
29568         }
29569         Roo.DialogManager.unregister(this);
29570     },
29571
29572     // private
29573     startMove : function(){
29574         if(this.proxyDrag){
29575             this.proxy.show();
29576         }
29577         if(this.constraintoviewport !== false){
29578             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29579         }
29580     },
29581
29582     // private
29583     endMove : function(){
29584         if(!this.proxyDrag){
29585             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29586         }else{
29587             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29588             this.proxy.hide();
29589         }
29590         this.refreshSize();
29591         this.adjustAssets();
29592         this.focus();
29593         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29594     },
29595
29596     /**
29597      * Brings this dialog to the front of any other visible dialogs
29598      * @return {Roo.BasicDialog} this
29599      */
29600     toFront : function(){
29601         Roo.DialogManager.bringToFront(this);
29602         return this;
29603     },
29604
29605     /**
29606      * Sends this dialog to the back (under) of any other visible dialogs
29607      * @return {Roo.BasicDialog} this
29608      */
29609     toBack : function(){
29610         Roo.DialogManager.sendToBack(this);
29611         return this;
29612     },
29613
29614     /**
29615      * Centers this dialog in the viewport
29616      * @return {Roo.BasicDialog} this
29617      */
29618     center : function(){
29619         var xy = this.el.getCenterXY(true);
29620         this.moveTo(xy[0], xy[1]);
29621         return this;
29622     },
29623
29624     /**
29625      * Moves the dialog's top-left corner to the specified point
29626      * @param {Number} x
29627      * @param {Number} y
29628      * @return {Roo.BasicDialog} this
29629      */
29630     moveTo : function(x, y){
29631         this.xy = [x,y];
29632         if(this.isVisible()){
29633             this.el.setXY(this.xy);
29634             this.adjustAssets();
29635         }
29636         return this;
29637     },
29638
29639     /**
29640      * Aligns the dialog to the specified element
29641      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29642      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29643      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29644      * @return {Roo.BasicDialog} this
29645      */
29646     alignTo : function(element, position, offsets){
29647         this.xy = this.el.getAlignToXY(element, position, offsets);
29648         if(this.isVisible()){
29649             this.el.setXY(this.xy);
29650             this.adjustAssets();
29651         }
29652         return this;
29653     },
29654
29655     /**
29656      * Anchors an element to another element and realigns it when the window is resized.
29657      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29658      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29659      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29660      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29661      * is a number, it is used as the buffer delay (defaults to 50ms).
29662      * @return {Roo.BasicDialog} this
29663      */
29664     anchorTo : function(el, alignment, offsets, monitorScroll){
29665         var action = function(){
29666             this.alignTo(el, alignment, offsets);
29667         };
29668         Roo.EventManager.onWindowResize(action, this);
29669         var tm = typeof monitorScroll;
29670         if(tm != 'undefined'){
29671             Roo.EventManager.on(window, 'scroll', action, this,
29672                 {buffer: tm == 'number' ? monitorScroll : 50});
29673         }
29674         action.call(this);
29675         return this;
29676     },
29677
29678     /**
29679      * Returns true if the dialog is visible
29680      * @return {Boolean}
29681      */
29682     isVisible : function(){
29683         return this.el.isVisible();
29684     },
29685
29686     // private
29687     animHide : function(callback){
29688         var b = Roo.get(this.animateTarget).getBox();
29689         this.proxy.show();
29690         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29691         this.el.hide();
29692         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29693                     this.hideEl.createDelegate(this, [callback]));
29694     },
29695
29696     /**
29697      * Hides the dialog.
29698      * @param {Function} callback (optional) Function to call when the dialog is hidden
29699      * @return {Roo.BasicDialog} this
29700      */
29701     hide : function(callback){
29702         if (this.fireEvent("beforehide", this) === false){
29703             return;
29704         }
29705         if(this.shadow){
29706             this.shadow.hide();
29707         }
29708         if(this.shim) {
29709           this.shim.hide();
29710         }
29711         // sometimes animateTarget seems to get set.. causing problems...
29712         // this just double checks..
29713         if(this.animateTarget && Roo.get(this.animateTarget)) {
29714            this.animHide(callback);
29715         }else{
29716             this.el.hide();
29717             this.hideEl(callback);
29718         }
29719         return this;
29720     },
29721
29722     // private
29723     hideEl : function(callback){
29724         this.proxy.hide();
29725         if(this.modal){
29726             this.mask.hide();
29727             Roo.get(document.body).removeClass("x-body-masked");
29728         }
29729         this.fireEvent("hide", this);
29730         if(typeof callback == "function"){
29731             callback();
29732         }
29733     },
29734
29735     // private
29736     hideAction : function(){
29737         this.setLeft("-10000px");
29738         this.setTop("-10000px");
29739         this.setStyle("visibility", "hidden");
29740     },
29741
29742     // private
29743     refreshSize : function(){
29744         this.size = this.el.getSize();
29745         this.xy = this.el.getXY();
29746         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29747     },
29748
29749     // private
29750     // z-index is managed by the DialogManager and may be overwritten at any time
29751     setZIndex : function(index){
29752         if(this.modal){
29753             this.mask.setStyle("z-index", index);
29754         }
29755         if(this.shim){
29756             this.shim.setStyle("z-index", ++index);
29757         }
29758         if(this.shadow){
29759             this.shadow.setZIndex(++index);
29760         }
29761         this.el.setStyle("z-index", ++index);
29762         if(this.proxy){
29763             this.proxy.setStyle("z-index", ++index);
29764         }
29765         if(this.resizer){
29766             this.resizer.proxy.setStyle("z-index", ++index);
29767         }
29768
29769         this.lastZIndex = index;
29770     },
29771
29772     /**
29773      * Returns the element for this dialog
29774      * @return {Roo.Element} The underlying dialog Element
29775      */
29776     getEl : function(){
29777         return this.el;
29778     }
29779 });
29780
29781 /**
29782  * @class Roo.DialogManager
29783  * Provides global access to BasicDialogs that have been created and
29784  * support for z-indexing (layering) multiple open dialogs.
29785  */
29786 Roo.DialogManager = function(){
29787     var list = {};
29788     var accessList = [];
29789     var front = null;
29790
29791     // private
29792     var sortDialogs = function(d1, d2){
29793         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29794     };
29795
29796     // private
29797     var orderDialogs = function(){
29798         accessList.sort(sortDialogs);
29799         var seed = Roo.DialogManager.zseed;
29800         for(var i = 0, len = accessList.length; i < len; i++){
29801             var dlg = accessList[i];
29802             if(dlg){
29803                 dlg.setZIndex(seed + (i*10));
29804             }
29805         }
29806     };
29807
29808     return {
29809         /**
29810          * The starting z-index for BasicDialogs (defaults to 9000)
29811          * @type Number The z-index value
29812          */
29813         zseed : 9000,
29814
29815         // private
29816         register : function(dlg){
29817             list[dlg.id] = dlg;
29818             accessList.push(dlg);
29819         },
29820
29821         // private
29822         unregister : function(dlg){
29823             delete list[dlg.id];
29824             var i=0;
29825             var len=0;
29826             if(!accessList.indexOf){
29827                 for(  i = 0, len = accessList.length; i < len; i++){
29828                     if(accessList[i] == dlg){
29829                         accessList.splice(i, 1);
29830                         return;
29831                     }
29832                 }
29833             }else{
29834                  i = accessList.indexOf(dlg);
29835                 if(i != -1){
29836                     accessList.splice(i, 1);
29837                 }
29838             }
29839         },
29840
29841         /**
29842          * Gets a registered dialog by id
29843          * @param {String/Object} id The id of the dialog or a dialog
29844          * @return {Roo.BasicDialog} this
29845          */
29846         get : function(id){
29847             return typeof id == "object" ? id : list[id];
29848         },
29849
29850         /**
29851          * Brings the specified dialog to the front
29852          * @param {String/Object} dlg The id of the dialog or a dialog
29853          * @return {Roo.BasicDialog} this
29854          */
29855         bringToFront : function(dlg){
29856             dlg = this.get(dlg);
29857             if(dlg != front){
29858                 front = dlg;
29859                 dlg._lastAccess = new Date().getTime();
29860                 orderDialogs();
29861             }
29862             return dlg;
29863         },
29864
29865         /**
29866          * Sends the specified dialog to the back
29867          * @param {String/Object} dlg The id of the dialog or a dialog
29868          * @return {Roo.BasicDialog} this
29869          */
29870         sendToBack : function(dlg){
29871             dlg = this.get(dlg);
29872             dlg._lastAccess = -(new Date().getTime());
29873             orderDialogs();
29874             return dlg;
29875         },
29876
29877         /**
29878          * Hides all dialogs
29879          */
29880         hideAll : function(){
29881             for(var id in list){
29882                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29883                     list[id].hide();
29884                 }
29885             }
29886         }
29887     };
29888 }();
29889
29890 /**
29891  * @class Roo.LayoutDialog
29892  * @extends Roo.BasicDialog
29893  * Dialog which provides adjustments for working with a layout in a Dialog.
29894  * Add your necessary layout config options to the dialog's config.<br>
29895  * Example usage (including a nested layout):
29896  * <pre><code>
29897 if(!dialog){
29898     dialog = new Roo.LayoutDialog("download-dlg", {
29899         modal: true,
29900         width:600,
29901         height:450,
29902         shadow:true,
29903         minWidth:500,
29904         minHeight:350,
29905         autoTabs:true,
29906         proxyDrag:true,
29907         // layout config merges with the dialog config
29908         center:{
29909             tabPosition: "top",
29910             alwaysShowTabs: true
29911         }
29912     });
29913     dialog.addKeyListener(27, dialog.hide, dialog);
29914     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29915     dialog.addButton("Build It!", this.getDownload, this);
29916
29917     // we can even add nested layouts
29918     var innerLayout = new Roo.BorderLayout("dl-inner", {
29919         east: {
29920             initialSize: 200,
29921             autoScroll:true,
29922             split:true
29923         },
29924         center: {
29925             autoScroll:true
29926         }
29927     });
29928     innerLayout.beginUpdate();
29929     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29930     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29931     innerLayout.endUpdate(true);
29932
29933     var layout = dialog.getLayout();
29934     layout.beginUpdate();
29935     layout.add("center", new Roo.ContentPanel("standard-panel",
29936                         {title: "Download the Source", fitToFrame:true}));
29937     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29938                {title: "Build your own roo.js"}));
29939     layout.getRegion("center").showPanel(sp);
29940     layout.endUpdate();
29941 }
29942 </code></pre>
29943     * @constructor
29944     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29945     * @param {Object} config configuration options
29946   */
29947 Roo.LayoutDialog = function(el, cfg){
29948     
29949     var config=  cfg;
29950     if (typeof(cfg) == 'undefined') {
29951         config = Roo.apply({}, el);
29952         // not sure why we use documentElement here.. - it should always be body.
29953         // IE7 borks horribly if we use documentElement.
29954         // webkit also does not like documentElement - it creates a body element...
29955         el = Roo.get( document.body || document.documentElement ).createChild();
29956         //config.autoCreate = true;
29957     }
29958     
29959     
29960     config.autoTabs = false;
29961     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29962     this.body.setStyle({overflow:"hidden", position:"relative"});
29963     this.layout = new Roo.BorderLayout(this.body.dom, config);
29964     this.layout.monitorWindowResize = false;
29965     this.el.addClass("x-dlg-auto-layout");
29966     // fix case when center region overwrites center function
29967     this.center = Roo.BasicDialog.prototype.center;
29968     this.on("show", this.layout.layout, this.layout, true);
29969     if (config.items) {
29970         var xitems = config.items;
29971         delete config.items;
29972         Roo.each(xitems, this.addxtype, this);
29973     }
29974     
29975     
29976 };
29977 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29978     /**
29979      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29980      * @deprecated
29981      */
29982     endUpdate : function(){
29983         this.layout.endUpdate();
29984     },
29985
29986     /**
29987      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29988      *  @deprecated
29989      */
29990     beginUpdate : function(){
29991         this.layout.beginUpdate();
29992     },
29993
29994     /**
29995      * Get the BorderLayout for this dialog
29996      * @return {Roo.BorderLayout}
29997      */
29998     getLayout : function(){
29999         return this.layout;
30000     },
30001
30002     showEl : function(){
30003         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30004         if(Roo.isIE7){
30005             this.layout.layout();
30006         }
30007     },
30008
30009     // private
30010     // Use the syncHeightBeforeShow config option to control this automatically
30011     syncBodyHeight : function(){
30012         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30013         if(this.layout){this.layout.layout();}
30014     },
30015     
30016       /**
30017      * Add an xtype element (actually adds to the layout.)
30018      * @return {Object} xdata xtype object data.
30019      */
30020     
30021     addxtype : function(c) {
30022         return this.layout.addxtype(c);
30023     }
30024 });/*
30025  * Based on:
30026  * Ext JS Library 1.1.1
30027  * Copyright(c) 2006-2007, Ext JS, LLC.
30028  *
30029  * Originally Released Under LGPL - original licence link has changed is not relivant.
30030  *
30031  * Fork - LGPL
30032  * <script type="text/javascript">
30033  */
30034  
30035 /**
30036  * @class Roo.MessageBox
30037  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30038  * Example usage:
30039  *<pre><code>
30040 // Basic alert:
30041 Roo.Msg.alert('Status', 'Changes saved successfully.');
30042
30043 // Prompt for user data:
30044 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30045     if (btn == 'ok'){
30046         // process text value...
30047     }
30048 });
30049
30050 // Show a dialog using config options:
30051 Roo.Msg.show({
30052    title:'Save Changes?',
30053    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30054    buttons: Roo.Msg.YESNOCANCEL,
30055    fn: processResult,
30056    animEl: 'elId'
30057 });
30058 </code></pre>
30059  * @singleton
30060  */
30061 Roo.MessageBox = function(){
30062     var dlg, opt, mask, waitTimer;
30063     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30064     var buttons, activeTextEl, bwidth;
30065
30066     // private
30067     var handleButton = function(button){
30068         dlg.hide();
30069         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30070     };
30071
30072     // private
30073     var handleHide = function(){
30074         if(opt && opt.cls){
30075             dlg.el.removeClass(opt.cls);
30076         }
30077         if(waitTimer){
30078             Roo.TaskMgr.stop(waitTimer);
30079             waitTimer = null;
30080         }
30081     };
30082
30083     // private
30084     var updateButtons = function(b){
30085         var width = 0;
30086         if(!b){
30087             buttons["ok"].hide();
30088             buttons["cancel"].hide();
30089             buttons["yes"].hide();
30090             buttons["no"].hide();
30091             dlg.footer.dom.style.display = 'none';
30092             return width;
30093         }
30094         dlg.footer.dom.style.display = '';
30095         for(var k in buttons){
30096             if(typeof buttons[k] != "function"){
30097                 if(b[k]){
30098                     buttons[k].show();
30099                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30100                     width += buttons[k].el.getWidth()+15;
30101                 }else{
30102                     buttons[k].hide();
30103                 }
30104             }
30105         }
30106         return width;
30107     };
30108
30109     // private
30110     var handleEsc = function(d, k, e){
30111         if(opt && opt.closable !== false){
30112             dlg.hide();
30113         }
30114         if(e){
30115             e.stopEvent();
30116         }
30117     };
30118
30119     return {
30120         /**
30121          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30122          * @return {Roo.BasicDialog} The BasicDialog element
30123          */
30124         getDialog : function(){
30125            if(!dlg){
30126                 dlg = new Roo.BasicDialog("x-msg-box", {
30127                     autoCreate : true,
30128                     shadow: true,
30129                     draggable: true,
30130                     resizable:false,
30131                     constraintoviewport:false,
30132                     fixedcenter:true,
30133                     collapsible : false,
30134                     shim:true,
30135                     modal: true,
30136                     width:400, height:100,
30137                     buttonAlign:"center",
30138                     closeClick : function(){
30139                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30140                             handleButton("no");
30141                         }else{
30142                             handleButton("cancel");
30143                         }
30144                     }
30145                 });
30146                 dlg.on("hide", handleHide);
30147                 mask = dlg.mask;
30148                 dlg.addKeyListener(27, handleEsc);
30149                 buttons = {};
30150                 var bt = this.buttonText;
30151                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30152                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30153                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30154                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30155                 bodyEl = dlg.body.createChild({
30156
30157                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
30158                 });
30159                 msgEl = bodyEl.dom.firstChild;
30160                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30161                 textboxEl.enableDisplayMode();
30162                 textboxEl.addKeyListener([10,13], function(){
30163                     if(dlg.isVisible() && opt && opt.buttons){
30164                         if(opt.buttons.ok){
30165                             handleButton("ok");
30166                         }else if(opt.buttons.yes){
30167                             handleButton("yes");
30168                         }
30169                     }
30170                 });
30171                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30172                 textareaEl.enableDisplayMode();
30173                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30174                 progressEl.enableDisplayMode();
30175                 var pf = progressEl.dom.firstChild;
30176                 if (pf) {
30177                     pp = Roo.get(pf.firstChild);
30178                     pp.setHeight(pf.offsetHeight);
30179                 }
30180                 
30181             }
30182             return dlg;
30183         },
30184
30185         /**
30186          * Updates the message box body text
30187          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30188          * the XHTML-compliant non-breaking space character '&amp;#160;')
30189          * @return {Roo.MessageBox} This message box
30190          */
30191         updateText : function(text){
30192             if(!dlg.isVisible() && !opt.width){
30193                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30194             }
30195             msgEl.innerHTML = text || '&#160;';
30196       
30197             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30198             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30199             var w = Math.max(
30200                     Math.min(opt.width || cw , this.maxWidth), 
30201                     Math.max(opt.minWidth || this.minWidth, bwidth)
30202             );
30203             if(opt.prompt){
30204                 activeTextEl.setWidth(w);
30205             }
30206             if(dlg.isVisible()){
30207                 dlg.fixedcenter = false;
30208             }
30209             // to big, make it scroll. = But as usual stupid IE does not support
30210             // !important..
30211             
30212             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30213                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30214                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30215             } else {
30216                 bodyEl.dom.style.height = '';
30217                 bodyEl.dom.style.overflowY = '';
30218             }
30219             if (cw > w) {
30220                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30221             } else {
30222                 bodyEl.dom.style.overflowX = '';
30223             }
30224             
30225             dlg.setContentSize(w, bodyEl.getHeight());
30226             if(dlg.isVisible()){
30227                 dlg.fixedcenter = true;
30228             }
30229             return this;
30230         },
30231
30232         /**
30233          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30234          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30235          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30236          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30237          * @return {Roo.MessageBox} This message box
30238          */
30239         updateProgress : function(value, text){
30240             if(text){
30241                 this.updateText(text);
30242             }
30243             if (pp) { // weird bug on my firefox - for some reason this is not defined
30244                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30245             }
30246             return this;
30247         },        
30248
30249         /**
30250          * Returns true if the message box is currently displayed
30251          * @return {Boolean} True if the message box is visible, else false
30252          */
30253         isVisible : function(){
30254             return dlg && dlg.isVisible();  
30255         },
30256
30257         /**
30258          * Hides the message box if it is displayed
30259          */
30260         hide : function(){
30261             if(this.isVisible()){
30262                 dlg.hide();
30263             }  
30264         },
30265
30266         /**
30267          * Displays a new message box, or reinitializes an existing message box, based on the config options
30268          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30269          * The following config object properties are supported:
30270          * <pre>
30271 Property    Type             Description
30272 ----------  ---------------  ------------------------------------------------------------------------------------
30273 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30274                                    closes (defaults to undefined)
30275 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30276                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30277 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30278                                    progress and wait dialogs will ignore this property and always hide the
30279                                    close button as they can only be closed programmatically.
30280 cls               String           A custom CSS class to apply to the message box element
30281 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30282                                    displayed (defaults to 75)
30283 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30284                                    function will be btn (the name of the button that was clicked, if applicable,
30285                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30286                                    Progress and wait dialogs will ignore this option since they do not respond to
30287                                    user actions and can only be closed programmatically, so any required function
30288                                    should be called by the same code after it closes the dialog.
30289 icon              String           A CSS class that provides a background image to be used as an icon for
30290                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30291 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30292 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30293 modal             Boolean          False to allow user interaction with the page while the message box is
30294                                    displayed (defaults to true)
30295 msg               String           A string that will replace the existing message box body text (defaults
30296                                    to the XHTML-compliant non-breaking space character '&#160;')
30297 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30298 progress          Boolean          True to display a progress bar (defaults to false)
30299 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30300 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30301 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30302 title             String           The title text
30303 value             String           The string value to set into the active textbox element if displayed
30304 wait              Boolean          True to display a progress bar (defaults to false)
30305 width             Number           The width of the dialog in pixels
30306 </pre>
30307          *
30308          * Example usage:
30309          * <pre><code>
30310 Roo.Msg.show({
30311    title: 'Address',
30312    msg: 'Please enter your address:',
30313    width: 300,
30314    buttons: Roo.MessageBox.OKCANCEL,
30315    multiline: true,
30316    fn: saveAddress,
30317    animEl: 'addAddressBtn'
30318 });
30319 </code></pre>
30320          * @param {Object} config Configuration options
30321          * @return {Roo.MessageBox} This message box
30322          */
30323         show : function(options)
30324         {
30325             
30326             // this causes nightmares if you show one dialog after another
30327             // especially on callbacks..
30328              
30329             if(this.isVisible()){
30330                 
30331                 this.hide();
30332                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30333                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30334                 Roo.log("New Dialog Message:" +  options.msg )
30335                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30336                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30337                 
30338             }
30339             var d = this.getDialog();
30340             opt = options;
30341             d.setTitle(opt.title || "&#160;");
30342             d.close.setDisplayed(opt.closable !== false);
30343             activeTextEl = textboxEl;
30344             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30345             if(opt.prompt){
30346                 if(opt.multiline){
30347                     textboxEl.hide();
30348                     textareaEl.show();
30349                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30350                         opt.multiline : this.defaultTextHeight);
30351                     activeTextEl = textareaEl;
30352                 }else{
30353                     textboxEl.show();
30354                     textareaEl.hide();
30355                 }
30356             }else{
30357                 textboxEl.hide();
30358                 textareaEl.hide();
30359             }
30360             progressEl.setDisplayed(opt.progress === true);
30361             this.updateProgress(0);
30362             activeTextEl.dom.value = opt.value || "";
30363             if(opt.prompt){
30364                 dlg.setDefaultButton(activeTextEl);
30365             }else{
30366                 var bs = opt.buttons;
30367                 var db = null;
30368                 if(bs && bs.ok){
30369                     db = buttons["ok"];
30370                 }else if(bs && bs.yes){
30371                     db = buttons["yes"];
30372                 }
30373                 dlg.setDefaultButton(db);
30374             }
30375             bwidth = updateButtons(opt.buttons);
30376             this.updateText(opt.msg);
30377             if(opt.cls){
30378                 d.el.addClass(opt.cls);
30379             }
30380             d.proxyDrag = opt.proxyDrag === true;
30381             d.modal = opt.modal !== false;
30382             d.mask = opt.modal !== false ? mask : false;
30383             if(!d.isVisible()){
30384                 // force it to the end of the z-index stack so it gets a cursor in FF
30385                 document.body.appendChild(dlg.el.dom);
30386                 d.animateTarget = null;
30387                 d.show(options.animEl);
30388             }
30389             return this;
30390         },
30391
30392         /**
30393          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30394          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30395          * and closing the message box when the process is complete.
30396          * @param {String} title The title bar text
30397          * @param {String} msg The message box body text
30398          * @return {Roo.MessageBox} This message box
30399          */
30400         progress : function(title, msg){
30401             this.show({
30402                 title : title,
30403                 msg : msg,
30404                 buttons: false,
30405                 progress:true,
30406                 closable:false,
30407                 minWidth: this.minProgressWidth,
30408                 modal : true
30409             });
30410             return this;
30411         },
30412
30413         /**
30414          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30415          * If a callback function is passed it will be called after the user clicks the button, and the
30416          * id of the button that was clicked will be passed as the only parameter to the callback
30417          * (could also be the top-right close button).
30418          * @param {String} title The title bar text
30419          * @param {String} msg The message box body text
30420          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30421          * @param {Object} scope (optional) The scope of the callback function
30422          * @return {Roo.MessageBox} This message box
30423          */
30424         alert : function(title, msg, fn, scope){
30425             this.show({
30426                 title : title,
30427                 msg : msg,
30428                 buttons: this.OK,
30429                 fn: fn,
30430                 scope : scope,
30431                 modal : true
30432             });
30433             return this;
30434         },
30435
30436         /**
30437          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30438          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30439          * You are responsible for closing the message box when the process is complete.
30440          * @param {String} msg The message box body text
30441          * @param {String} title (optional) The title bar text
30442          * @return {Roo.MessageBox} This message box
30443          */
30444         wait : function(msg, title){
30445             this.show({
30446                 title : title,
30447                 msg : msg,
30448                 buttons: false,
30449                 closable:false,
30450                 progress:true,
30451                 modal:true,
30452                 width:300,
30453                 wait:true
30454             });
30455             waitTimer = Roo.TaskMgr.start({
30456                 run: function(i){
30457                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30458                 },
30459                 interval: 1000
30460             });
30461             return this;
30462         },
30463
30464         /**
30465          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30466          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30467          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30468          * @param {String} title The title bar text
30469          * @param {String} msg The message box body text
30470          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30471          * @param {Object} scope (optional) The scope of the callback function
30472          * @return {Roo.MessageBox} This message box
30473          */
30474         confirm : function(title, msg, fn, scope){
30475             this.show({
30476                 title : title,
30477                 msg : msg,
30478                 buttons: this.YESNO,
30479                 fn: fn,
30480                 scope : scope,
30481                 modal : true
30482             });
30483             return this;
30484         },
30485
30486         /**
30487          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30488          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30489          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30490          * (could also be the top-right close button) and the text that was entered will be passed as the two
30491          * parameters to the callback.
30492          * @param {String} title The title bar text
30493          * @param {String} msg The message box body text
30494          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30495          * @param {Object} scope (optional) The scope of the callback function
30496          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30497          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30498          * @return {Roo.MessageBox} This message box
30499          */
30500         prompt : function(title, msg, fn, scope, multiline){
30501             this.show({
30502                 title : title,
30503                 msg : msg,
30504                 buttons: this.OKCANCEL,
30505                 fn: fn,
30506                 minWidth:250,
30507                 scope : scope,
30508                 prompt:true,
30509                 multiline: multiline,
30510                 modal : true
30511             });
30512             return this;
30513         },
30514
30515         /**
30516          * Button config that displays a single OK button
30517          * @type Object
30518          */
30519         OK : {ok:true},
30520         /**
30521          * Button config that displays Yes and No buttons
30522          * @type Object
30523          */
30524         YESNO : {yes:true, no:true},
30525         /**
30526          * Button config that displays OK and Cancel buttons
30527          * @type Object
30528          */
30529         OKCANCEL : {ok:true, cancel:true},
30530         /**
30531          * Button config that displays Yes, No and Cancel buttons
30532          * @type Object
30533          */
30534         YESNOCANCEL : {yes:true, no:true, cancel:true},
30535
30536         /**
30537          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30538          * @type Number
30539          */
30540         defaultTextHeight : 75,
30541         /**
30542          * The maximum width in pixels of the message box (defaults to 600)
30543          * @type Number
30544          */
30545         maxWidth : 600,
30546         /**
30547          * The minimum width in pixels of the message box (defaults to 100)
30548          * @type Number
30549          */
30550         minWidth : 100,
30551         /**
30552          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30553          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30554          * @type Number
30555          */
30556         minProgressWidth : 250,
30557         /**
30558          * An object containing the default button text strings that can be overriden for localized language support.
30559          * Supported properties are: ok, cancel, yes and no.
30560          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30561          * @type Object
30562          */
30563         buttonText : {
30564             ok : "OK",
30565             cancel : "Cancel",
30566             yes : "Yes",
30567             no : "No"
30568         }
30569     };
30570 }();
30571
30572 /**
30573  * Shorthand for {@link Roo.MessageBox}
30574  */
30575 Roo.Msg = Roo.MessageBox;/*
30576  * Based on:
30577  * Ext JS Library 1.1.1
30578  * Copyright(c) 2006-2007, Ext JS, LLC.
30579  *
30580  * Originally Released Under LGPL - original licence link has changed is not relivant.
30581  *
30582  * Fork - LGPL
30583  * <script type="text/javascript">
30584  */
30585 /**
30586  * @class Roo.QuickTips
30587  * Provides attractive and customizable tooltips for any element.
30588  * @singleton
30589  */
30590 Roo.QuickTips = function(){
30591     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30592     var ce, bd, xy, dd;
30593     var visible = false, disabled = true, inited = false;
30594     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30595     
30596     var onOver = function(e){
30597         if(disabled){
30598             return;
30599         }
30600         var t = e.getTarget();
30601         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30602             return;
30603         }
30604         if(ce && t == ce.el){
30605             clearTimeout(hideProc);
30606             return;
30607         }
30608         if(t && tagEls[t.id]){
30609             tagEls[t.id].el = t;
30610             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30611             return;
30612         }
30613         var ttp, et = Roo.fly(t);
30614         var ns = cfg.namespace;
30615         if(tm.interceptTitles && t.title){
30616             ttp = t.title;
30617             t.qtip = ttp;
30618             t.removeAttribute("title");
30619             e.preventDefault();
30620         }else{
30621             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30622         }
30623         if(ttp){
30624             showProc = show.defer(tm.showDelay, tm, [{
30625                 el: t, 
30626                 text: ttp, 
30627                 width: et.getAttributeNS(ns, cfg.width),
30628                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30629                 title: et.getAttributeNS(ns, cfg.title),
30630                     cls: et.getAttributeNS(ns, cfg.cls)
30631             }]);
30632         }
30633     };
30634     
30635     var onOut = function(e){
30636         clearTimeout(showProc);
30637         var t = e.getTarget();
30638         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30639             hideProc = setTimeout(hide, tm.hideDelay);
30640         }
30641     };
30642     
30643     var onMove = function(e){
30644         if(disabled){
30645             return;
30646         }
30647         xy = e.getXY();
30648         xy[1] += 18;
30649         if(tm.trackMouse && ce){
30650             el.setXY(xy);
30651         }
30652     };
30653     
30654     var onDown = function(e){
30655         clearTimeout(showProc);
30656         clearTimeout(hideProc);
30657         if(!e.within(el)){
30658             if(tm.hideOnClick){
30659                 hide();
30660                 tm.disable();
30661                 tm.enable.defer(100, tm);
30662             }
30663         }
30664     };
30665     
30666     var getPad = function(){
30667         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30668     };
30669
30670     var show = function(o){
30671         if(disabled){
30672             return;
30673         }
30674         clearTimeout(dismissProc);
30675         ce = o;
30676         if(removeCls){ // in case manually hidden
30677             el.removeClass(removeCls);
30678             removeCls = null;
30679         }
30680         if(ce.cls){
30681             el.addClass(ce.cls);
30682             removeCls = ce.cls;
30683         }
30684         if(ce.title){
30685             tipTitle.update(ce.title);
30686             tipTitle.show();
30687         }else{
30688             tipTitle.update('');
30689             tipTitle.hide();
30690         }
30691         el.dom.style.width  = tm.maxWidth+'px';
30692         //tipBody.dom.style.width = '';
30693         tipBodyText.update(o.text);
30694         var p = getPad(), w = ce.width;
30695         if(!w){
30696             var td = tipBodyText.dom;
30697             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30698             if(aw > tm.maxWidth){
30699                 w = tm.maxWidth;
30700             }else if(aw < tm.minWidth){
30701                 w = tm.minWidth;
30702             }else{
30703                 w = aw;
30704             }
30705         }
30706         //tipBody.setWidth(w);
30707         el.setWidth(parseInt(w, 10) + p);
30708         if(ce.autoHide === false){
30709             close.setDisplayed(true);
30710             if(dd){
30711                 dd.unlock();
30712             }
30713         }else{
30714             close.setDisplayed(false);
30715             if(dd){
30716                 dd.lock();
30717             }
30718         }
30719         if(xy){
30720             el.avoidY = xy[1]-18;
30721             el.setXY(xy);
30722         }
30723         if(tm.animate){
30724             el.setOpacity(.1);
30725             el.setStyle("visibility", "visible");
30726             el.fadeIn({callback: afterShow});
30727         }else{
30728             afterShow();
30729         }
30730     };
30731     
30732     var afterShow = function(){
30733         if(ce){
30734             el.show();
30735             esc.enable();
30736             if(tm.autoDismiss && ce.autoHide !== false){
30737                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30738             }
30739         }
30740     };
30741     
30742     var hide = function(noanim){
30743         clearTimeout(dismissProc);
30744         clearTimeout(hideProc);
30745         ce = null;
30746         if(el.isVisible()){
30747             esc.disable();
30748             if(noanim !== true && tm.animate){
30749                 el.fadeOut({callback: afterHide});
30750             }else{
30751                 afterHide();
30752             } 
30753         }
30754     };
30755     
30756     var afterHide = function(){
30757         el.hide();
30758         if(removeCls){
30759             el.removeClass(removeCls);
30760             removeCls = null;
30761         }
30762     };
30763     
30764     return {
30765         /**
30766         * @cfg {Number} minWidth
30767         * The minimum width of the quick tip (defaults to 40)
30768         */
30769        minWidth : 40,
30770         /**
30771         * @cfg {Number} maxWidth
30772         * The maximum width of the quick tip (defaults to 300)
30773         */
30774        maxWidth : 300,
30775         /**
30776         * @cfg {Boolean} interceptTitles
30777         * True to automatically use the element's DOM title value if available (defaults to false)
30778         */
30779        interceptTitles : false,
30780         /**
30781         * @cfg {Boolean} trackMouse
30782         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30783         */
30784        trackMouse : false,
30785         /**
30786         * @cfg {Boolean} hideOnClick
30787         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30788         */
30789        hideOnClick : true,
30790         /**
30791         * @cfg {Number} showDelay
30792         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30793         */
30794        showDelay : 500,
30795         /**
30796         * @cfg {Number} hideDelay
30797         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30798         */
30799        hideDelay : 200,
30800         /**
30801         * @cfg {Boolean} autoHide
30802         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30803         * Used in conjunction with hideDelay.
30804         */
30805        autoHide : true,
30806         /**
30807         * @cfg {Boolean}
30808         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30809         * (defaults to true).  Used in conjunction with autoDismissDelay.
30810         */
30811        autoDismiss : true,
30812         /**
30813         * @cfg {Number}
30814         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30815         */
30816        autoDismissDelay : 5000,
30817        /**
30818         * @cfg {Boolean} animate
30819         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30820         */
30821        animate : false,
30822
30823        /**
30824         * @cfg {String} title
30825         * Title text to display (defaults to '').  This can be any valid HTML markup.
30826         */
30827         title: '',
30828        /**
30829         * @cfg {String} text
30830         * Body text to display (defaults to '').  This can be any valid HTML markup.
30831         */
30832         text : '',
30833        /**
30834         * @cfg {String} cls
30835         * A CSS class to apply to the base quick tip element (defaults to '').
30836         */
30837         cls : '',
30838        /**
30839         * @cfg {Number} width
30840         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30841         * minWidth or maxWidth.
30842         */
30843         width : null,
30844
30845     /**
30846      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30847      * or display QuickTips in a page.
30848      */
30849        init : function(){
30850           tm = Roo.QuickTips;
30851           cfg = tm.tagConfig;
30852           if(!inited){
30853               if(!Roo.isReady){ // allow calling of init() before onReady
30854                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30855                   return;
30856               }
30857               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30858               el.fxDefaults = {stopFx: true};
30859               // maximum custom styling
30860               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30861               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30862               tipTitle = el.child('h3');
30863               tipTitle.enableDisplayMode("block");
30864               tipBody = el.child('div.x-tip-bd');
30865               tipBodyText = el.child('div.x-tip-bd-inner');
30866               //bdLeft = el.child('div.x-tip-bd-left');
30867               //bdRight = el.child('div.x-tip-bd-right');
30868               close = el.child('div.x-tip-close');
30869               close.enableDisplayMode("block");
30870               close.on("click", hide);
30871               var d = Roo.get(document);
30872               d.on("mousedown", onDown);
30873               d.on("mouseover", onOver);
30874               d.on("mouseout", onOut);
30875               d.on("mousemove", onMove);
30876               esc = d.addKeyListener(27, hide);
30877               esc.disable();
30878               if(Roo.dd.DD){
30879                   dd = el.initDD("default", null, {
30880                       onDrag : function(){
30881                           el.sync();  
30882                       }
30883                   });
30884                   dd.setHandleElId(tipTitle.id);
30885                   dd.lock();
30886               }
30887               inited = true;
30888           }
30889           this.enable(); 
30890        },
30891
30892     /**
30893      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30894      * are supported:
30895      * <pre>
30896 Property    Type                   Description
30897 ----------  ---------------------  ------------------------------------------------------------------------
30898 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30899      * </ul>
30900      * @param {Object} config The config object
30901      */
30902        register : function(config){
30903            var cs = config instanceof Array ? config : arguments;
30904            for(var i = 0, len = cs.length; i < len; i++) {
30905                var c = cs[i];
30906                var target = c.target;
30907                if(target){
30908                    if(target instanceof Array){
30909                        for(var j = 0, jlen = target.length; j < jlen; j++){
30910                            tagEls[target[j]] = c;
30911                        }
30912                    }else{
30913                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30914                    }
30915                }
30916            }
30917        },
30918
30919     /**
30920      * Removes this quick tip from its element and destroys it.
30921      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30922      */
30923        unregister : function(el){
30924            delete tagEls[Roo.id(el)];
30925        },
30926
30927     /**
30928      * Enable this quick tip.
30929      */
30930        enable : function(){
30931            if(inited && disabled){
30932                locks.pop();
30933                if(locks.length < 1){
30934                    disabled = false;
30935                }
30936            }
30937        },
30938
30939     /**
30940      * Disable this quick tip.
30941      */
30942        disable : function(){
30943           disabled = true;
30944           clearTimeout(showProc);
30945           clearTimeout(hideProc);
30946           clearTimeout(dismissProc);
30947           if(ce){
30948               hide(true);
30949           }
30950           locks.push(1);
30951        },
30952
30953     /**
30954      * Returns true if the quick tip is enabled, else false.
30955      */
30956        isEnabled : function(){
30957             return !disabled;
30958        },
30959
30960         // private
30961        tagConfig : {
30962            namespace : "ext",
30963            attribute : "qtip",
30964            width : "width",
30965            target : "target",
30966            title : "qtitle",
30967            hide : "hide",
30968            cls : "qclass"
30969        }
30970    };
30971 }();
30972
30973 // backwards compat
30974 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30975  * Based on:
30976  * Ext JS Library 1.1.1
30977  * Copyright(c) 2006-2007, Ext JS, LLC.
30978  *
30979  * Originally Released Under LGPL - original licence link has changed is not relivant.
30980  *
30981  * Fork - LGPL
30982  * <script type="text/javascript">
30983  */
30984  
30985
30986 /**
30987  * @class Roo.tree.TreePanel
30988  * @extends Roo.data.Tree
30989
30990  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30991  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30992  * @cfg {Boolean} enableDD true to enable drag and drop
30993  * @cfg {Boolean} enableDrag true to enable just drag
30994  * @cfg {Boolean} enableDrop true to enable just drop
30995  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30996  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30997  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30998  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30999  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31000  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31001  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31002  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31003  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31004  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31005  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31006  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31007  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31008  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31009  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
31010  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
31011  * 
31012  * @constructor
31013  * @param {String/HTMLElement/Element} el The container element
31014  * @param {Object} config
31015  */
31016 Roo.tree.TreePanel = function(el, config){
31017     var root = false;
31018     var loader = false;
31019     if (config.root) {
31020         root = config.root;
31021         delete config.root;
31022     }
31023     if (config.loader) {
31024         loader = config.loader;
31025         delete config.loader;
31026     }
31027     
31028     Roo.apply(this, config);
31029     Roo.tree.TreePanel.superclass.constructor.call(this);
31030     this.el = Roo.get(el);
31031     this.el.addClass('x-tree');
31032     //console.log(root);
31033     if (root) {
31034         this.setRootNode( Roo.factory(root, Roo.tree));
31035     }
31036     if (loader) {
31037         this.loader = Roo.factory(loader, Roo.tree);
31038     }
31039    /**
31040     * Read-only. The id of the container element becomes this TreePanel's id.
31041     */
31042     this.id = this.el.id;
31043     this.addEvents({
31044         /**
31045         * @event beforeload
31046         * Fires before a node is loaded, return false to cancel
31047         * @param {Node} node The node being loaded
31048         */
31049         "beforeload" : true,
31050         /**
31051         * @event load
31052         * Fires when a node is loaded
31053         * @param {Node} node The node that was loaded
31054         */
31055         "load" : true,
31056         /**
31057         * @event textchange
31058         * Fires when the text for a node is changed
31059         * @param {Node} node The node
31060         * @param {String} text The new text
31061         * @param {String} oldText The old text
31062         */
31063         "textchange" : true,
31064         /**
31065         * @event beforeexpand
31066         * Fires before a node is expanded, return false to cancel.
31067         * @param {Node} node The node
31068         * @param {Boolean} deep
31069         * @param {Boolean} anim
31070         */
31071         "beforeexpand" : true,
31072         /**
31073         * @event beforecollapse
31074         * Fires before a node is collapsed, return false to cancel.
31075         * @param {Node} node The node
31076         * @param {Boolean} deep
31077         * @param {Boolean} anim
31078         */
31079         "beforecollapse" : true,
31080         /**
31081         * @event expand
31082         * Fires when a node is expanded
31083         * @param {Node} node The node
31084         */
31085         "expand" : true,
31086         /**
31087         * @event disabledchange
31088         * Fires when the disabled status of a node changes
31089         * @param {Node} node The node
31090         * @param {Boolean} disabled
31091         */
31092         "disabledchange" : true,
31093         /**
31094         * @event collapse
31095         * Fires when a node is collapsed
31096         * @param {Node} node The node
31097         */
31098         "collapse" : true,
31099         /**
31100         * @event beforeclick
31101         * Fires before click processing on a node. Return false to cancel the default action.
31102         * @param {Node} node The node
31103         * @param {Roo.EventObject} e The event object
31104         */
31105         "beforeclick":true,
31106         /**
31107         * @event checkchange
31108         * Fires when a node with a checkbox's checked property changes
31109         * @param {Node} this This node
31110         * @param {Boolean} checked
31111         */
31112         "checkchange":true,
31113         /**
31114         * @event click
31115         * Fires when a node is clicked
31116         * @param {Node} node The node
31117         * @param {Roo.EventObject} e The event object
31118         */
31119         "click":true,
31120         /**
31121         * @event dblclick
31122         * Fires when a node is double clicked
31123         * @param {Node} node The node
31124         * @param {Roo.EventObject} e The event object
31125         */
31126         "dblclick":true,
31127         /**
31128         * @event contextmenu
31129         * Fires when a node is right clicked
31130         * @param {Node} node The node
31131         * @param {Roo.EventObject} e The event object
31132         */
31133         "contextmenu":true,
31134         /**
31135         * @event beforechildrenrendered
31136         * Fires right before the child nodes for a node are rendered
31137         * @param {Node} node The node
31138         */
31139         "beforechildrenrendered":true,
31140         /**
31141         * @event startdrag
31142         * Fires when a node starts being dragged
31143         * @param {Roo.tree.TreePanel} this
31144         * @param {Roo.tree.TreeNode} node
31145         * @param {event} e The raw browser event
31146         */ 
31147        "startdrag" : true,
31148        /**
31149         * @event enddrag
31150         * Fires when a drag operation is complete
31151         * @param {Roo.tree.TreePanel} this
31152         * @param {Roo.tree.TreeNode} node
31153         * @param {event} e The raw browser event
31154         */
31155        "enddrag" : true,
31156        /**
31157         * @event dragdrop
31158         * Fires when a dragged node is dropped on a valid DD target
31159         * @param {Roo.tree.TreePanel} this
31160         * @param {Roo.tree.TreeNode} node
31161         * @param {DD} dd The dd it was dropped on
31162         * @param {event} e The raw browser event
31163         */
31164        "dragdrop" : true,
31165        /**
31166         * @event beforenodedrop
31167         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31168         * passed to handlers has the following properties:<br />
31169         * <ul style="padding:5px;padding-left:16px;">
31170         * <li>tree - The TreePanel</li>
31171         * <li>target - The node being targeted for the drop</li>
31172         * <li>data - The drag data from the drag source</li>
31173         * <li>point - The point of the drop - append, above or below</li>
31174         * <li>source - The drag source</li>
31175         * <li>rawEvent - Raw mouse event</li>
31176         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31177         * to be inserted by setting them on this object.</li>
31178         * <li>cancel - Set this to true to cancel the drop.</li>
31179         * </ul>
31180         * @param {Object} dropEvent
31181         */
31182        "beforenodedrop" : true,
31183        /**
31184         * @event nodedrop
31185         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31186         * passed to handlers has the following properties:<br />
31187         * <ul style="padding:5px;padding-left:16px;">
31188         * <li>tree - The TreePanel</li>
31189         * <li>target - The node being targeted for the drop</li>
31190         * <li>data - The drag data from the drag source</li>
31191         * <li>point - The point of the drop - append, above or below</li>
31192         * <li>source - The drag source</li>
31193         * <li>rawEvent - Raw mouse event</li>
31194         * <li>dropNode - Dropped node(s).</li>
31195         * </ul>
31196         * @param {Object} dropEvent
31197         */
31198        "nodedrop" : true,
31199         /**
31200         * @event nodedragover
31201         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31202         * passed to handlers has the following properties:<br />
31203         * <ul style="padding:5px;padding-left:16px;">
31204         * <li>tree - The TreePanel</li>
31205         * <li>target - The node being targeted for the drop</li>
31206         * <li>data - The drag data from the drag source</li>
31207         * <li>point - The point of the drop - append, above or below</li>
31208         * <li>source - The drag source</li>
31209         * <li>rawEvent - Raw mouse event</li>
31210         * <li>dropNode - Drop node(s) provided by the source.</li>
31211         * <li>cancel - Set this to true to signal drop not allowed.</li>
31212         * </ul>
31213         * @param {Object} dragOverEvent
31214         */
31215        "nodedragover" : true
31216         
31217     });
31218     if(this.singleExpand){
31219        this.on("beforeexpand", this.restrictExpand, this);
31220     }
31221     if (this.editor) {
31222         this.editor.tree = this;
31223         this.editor = Roo.factory(this.editor, Roo.tree);
31224     }
31225     
31226     if (this.selModel) {
31227         this.selModel = Roo.factory(this.selModel, Roo.tree);
31228     }
31229    
31230 };
31231 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31232     rootVisible : true,
31233     animate: Roo.enableFx,
31234     lines : true,
31235     enableDD : false,
31236     hlDrop : Roo.enableFx,
31237   
31238     renderer: false,
31239     
31240     rendererTip: false,
31241     // private
31242     restrictExpand : function(node){
31243         var p = node.parentNode;
31244         if(p){
31245             if(p.expandedChild && p.expandedChild.parentNode == p){
31246                 p.expandedChild.collapse();
31247             }
31248             p.expandedChild = node;
31249         }
31250     },
31251
31252     // private override
31253     setRootNode : function(node){
31254         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31255         if(!this.rootVisible){
31256             node.ui = new Roo.tree.RootTreeNodeUI(node);
31257         }
31258         return node;
31259     },
31260
31261     /**
31262      * Returns the container element for this TreePanel
31263      */
31264     getEl : function(){
31265         return this.el;
31266     },
31267
31268     /**
31269      * Returns the default TreeLoader for this TreePanel
31270      */
31271     getLoader : function(){
31272         return this.loader;
31273     },
31274
31275     /**
31276      * Expand all nodes
31277      */
31278     expandAll : function(){
31279         this.root.expand(true);
31280     },
31281
31282     /**
31283      * Collapse all nodes
31284      */
31285     collapseAll : function(){
31286         this.root.collapse(true);
31287     },
31288
31289     /**
31290      * Returns the selection model used by this TreePanel
31291      */
31292     getSelectionModel : function(){
31293         if(!this.selModel){
31294             this.selModel = new Roo.tree.DefaultSelectionModel();
31295         }
31296         return this.selModel;
31297     },
31298
31299     /**
31300      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31301      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31302      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31303      * @return {Array}
31304      */
31305     getChecked : function(a, startNode){
31306         startNode = startNode || this.root;
31307         var r = [];
31308         var f = function(){
31309             if(this.attributes.checked){
31310                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31311             }
31312         }
31313         startNode.cascade(f);
31314         return r;
31315     },
31316
31317     /**
31318      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31319      * @param {String} path
31320      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31321      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31322      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31323      */
31324     expandPath : function(path, attr, callback){
31325         attr = attr || "id";
31326         var keys = path.split(this.pathSeparator);
31327         var curNode = this.root;
31328         if(curNode.attributes[attr] != keys[1]){ // invalid root
31329             if(callback){
31330                 callback(false, null);
31331             }
31332             return;
31333         }
31334         var index = 1;
31335         var f = function(){
31336             if(++index == keys.length){
31337                 if(callback){
31338                     callback(true, curNode);
31339                 }
31340                 return;
31341             }
31342             var c = curNode.findChild(attr, keys[index]);
31343             if(!c){
31344                 if(callback){
31345                     callback(false, curNode);
31346                 }
31347                 return;
31348             }
31349             curNode = c;
31350             c.expand(false, false, f);
31351         };
31352         curNode.expand(false, false, f);
31353     },
31354
31355     /**
31356      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31357      * @param {String} path
31358      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31359      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31360      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31361      */
31362     selectPath : function(path, attr, callback){
31363         attr = attr || "id";
31364         var keys = path.split(this.pathSeparator);
31365         var v = keys.pop();
31366         if(keys.length > 0){
31367             var f = function(success, node){
31368                 if(success && node){
31369                     var n = node.findChild(attr, v);
31370                     if(n){
31371                         n.select();
31372                         if(callback){
31373                             callback(true, n);
31374                         }
31375                     }else if(callback){
31376                         callback(false, n);
31377                     }
31378                 }else{
31379                     if(callback){
31380                         callback(false, n);
31381                     }
31382                 }
31383             };
31384             this.expandPath(keys.join(this.pathSeparator), attr, f);
31385         }else{
31386             this.root.select();
31387             if(callback){
31388                 callback(true, this.root);
31389             }
31390         }
31391     },
31392
31393     getTreeEl : function(){
31394         return this.el;
31395     },
31396
31397     /**
31398      * Trigger rendering of this TreePanel
31399      */
31400     render : function(){
31401         if (this.innerCt) {
31402             return this; // stop it rendering more than once!!
31403         }
31404         
31405         this.innerCt = this.el.createChild({tag:"ul",
31406                cls:"x-tree-root-ct " +
31407                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31408
31409         if(this.containerScroll){
31410             Roo.dd.ScrollManager.register(this.el);
31411         }
31412         if((this.enableDD || this.enableDrop) && !this.dropZone){
31413            /**
31414             * The dropZone used by this tree if drop is enabled
31415             * @type Roo.tree.TreeDropZone
31416             */
31417              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31418                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31419            });
31420         }
31421         if((this.enableDD || this.enableDrag) && !this.dragZone){
31422            /**
31423             * The dragZone used by this tree if drag is enabled
31424             * @type Roo.tree.TreeDragZone
31425             */
31426             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31427                ddGroup: this.ddGroup || "TreeDD",
31428                scroll: this.ddScroll
31429            });
31430         }
31431         this.getSelectionModel().init(this);
31432         if (!this.root) {
31433             console.log("ROOT not set in tree");
31434             return;
31435         }
31436         this.root.render();
31437         if(!this.rootVisible){
31438             this.root.renderChildren();
31439         }
31440         return this;
31441     }
31442 });/*
31443  * Based on:
31444  * Ext JS Library 1.1.1
31445  * Copyright(c) 2006-2007, Ext JS, LLC.
31446  *
31447  * Originally Released Under LGPL - original licence link has changed is not relivant.
31448  *
31449  * Fork - LGPL
31450  * <script type="text/javascript">
31451  */
31452  
31453
31454 /**
31455  * @class Roo.tree.DefaultSelectionModel
31456  * @extends Roo.util.Observable
31457  * The default single selection for a TreePanel.
31458  * @param {Object} cfg Configuration
31459  */
31460 Roo.tree.DefaultSelectionModel = function(cfg){
31461    this.selNode = null;
31462    
31463    
31464    
31465    this.addEvents({
31466        /**
31467         * @event selectionchange
31468         * Fires when the selected node changes
31469         * @param {DefaultSelectionModel} this
31470         * @param {TreeNode} node the new selection
31471         */
31472        "selectionchange" : true,
31473
31474        /**
31475         * @event beforeselect
31476         * Fires before the selected node changes, return false to cancel the change
31477         * @param {DefaultSelectionModel} this
31478         * @param {TreeNode} node the new selection
31479         * @param {TreeNode} node the old selection
31480         */
31481        "beforeselect" : true
31482    });
31483    
31484     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31485 };
31486
31487 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31488     init : function(tree){
31489         this.tree = tree;
31490         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31491         tree.on("click", this.onNodeClick, this);
31492     },
31493     
31494     onNodeClick : function(node, e){
31495         if (e.ctrlKey && this.selNode == node)  {
31496             this.unselect(node);
31497             return;
31498         }
31499         this.select(node);
31500     },
31501     
31502     /**
31503      * Select a node.
31504      * @param {TreeNode} node The node to select
31505      * @return {TreeNode} The selected node
31506      */
31507     select : function(node){
31508         var last = this.selNode;
31509         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31510             if(last){
31511                 last.ui.onSelectedChange(false);
31512             }
31513             this.selNode = node;
31514             node.ui.onSelectedChange(true);
31515             this.fireEvent("selectionchange", this, node, last);
31516         }
31517         return node;
31518     },
31519     
31520     /**
31521      * Deselect a node.
31522      * @param {TreeNode} node The node to unselect
31523      */
31524     unselect : function(node){
31525         if(this.selNode == node){
31526             this.clearSelections();
31527         }    
31528     },
31529     
31530     /**
31531      * Clear all selections
31532      */
31533     clearSelections : function(){
31534         var n = this.selNode;
31535         if(n){
31536             n.ui.onSelectedChange(false);
31537             this.selNode = null;
31538             this.fireEvent("selectionchange", this, null);
31539         }
31540         return n;
31541     },
31542     
31543     /**
31544      * Get the selected node
31545      * @return {TreeNode} The selected node
31546      */
31547     getSelectedNode : function(){
31548         return this.selNode;    
31549     },
31550     
31551     /**
31552      * Returns true if the node is selected
31553      * @param {TreeNode} node The node to check
31554      * @return {Boolean}
31555      */
31556     isSelected : function(node){
31557         return this.selNode == node;  
31558     },
31559
31560     /**
31561      * Selects the node above the selected node in the tree, intelligently walking the nodes
31562      * @return TreeNode The new selection
31563      */
31564     selectPrevious : function(){
31565         var s = this.selNode || this.lastSelNode;
31566         if(!s){
31567             return null;
31568         }
31569         var ps = s.previousSibling;
31570         if(ps){
31571             if(!ps.isExpanded() || ps.childNodes.length < 1){
31572                 return this.select(ps);
31573             } else{
31574                 var lc = ps.lastChild;
31575                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31576                     lc = lc.lastChild;
31577                 }
31578                 return this.select(lc);
31579             }
31580         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31581             return this.select(s.parentNode);
31582         }
31583         return null;
31584     },
31585
31586     /**
31587      * Selects the node above the selected node in the tree, intelligently walking the nodes
31588      * @return TreeNode The new selection
31589      */
31590     selectNext : function(){
31591         var s = this.selNode || this.lastSelNode;
31592         if(!s){
31593             return null;
31594         }
31595         if(s.firstChild && s.isExpanded()){
31596              return this.select(s.firstChild);
31597          }else if(s.nextSibling){
31598              return this.select(s.nextSibling);
31599          }else if(s.parentNode){
31600             var newS = null;
31601             s.parentNode.bubble(function(){
31602                 if(this.nextSibling){
31603                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31604                     return false;
31605                 }
31606             });
31607             return newS;
31608          }
31609         return null;
31610     },
31611
31612     onKeyDown : function(e){
31613         var s = this.selNode || this.lastSelNode;
31614         // undesirable, but required
31615         var sm = this;
31616         if(!s){
31617             return;
31618         }
31619         var k = e.getKey();
31620         switch(k){
31621              case e.DOWN:
31622                  e.stopEvent();
31623                  this.selectNext();
31624              break;
31625              case e.UP:
31626                  e.stopEvent();
31627                  this.selectPrevious();
31628              break;
31629              case e.RIGHT:
31630                  e.preventDefault();
31631                  if(s.hasChildNodes()){
31632                      if(!s.isExpanded()){
31633                          s.expand();
31634                      }else if(s.firstChild){
31635                          this.select(s.firstChild, e);
31636                      }
31637                  }
31638              break;
31639              case e.LEFT:
31640                  e.preventDefault();
31641                  if(s.hasChildNodes() && s.isExpanded()){
31642                      s.collapse();
31643                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31644                      this.select(s.parentNode, e);
31645                  }
31646              break;
31647         };
31648     }
31649 });
31650
31651 /**
31652  * @class Roo.tree.MultiSelectionModel
31653  * @extends Roo.util.Observable
31654  * Multi selection for a TreePanel.
31655  * @param {Object} cfg Configuration
31656  */
31657 Roo.tree.MultiSelectionModel = function(){
31658    this.selNodes = [];
31659    this.selMap = {};
31660    this.addEvents({
31661        /**
31662         * @event selectionchange
31663         * Fires when the selected nodes change
31664         * @param {MultiSelectionModel} this
31665         * @param {Array} nodes Array of the selected nodes
31666         */
31667        "selectionchange" : true
31668    });
31669    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31670    
31671 };
31672
31673 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31674     init : function(tree){
31675         this.tree = tree;
31676         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31677         tree.on("click", this.onNodeClick, this);
31678     },
31679     
31680     onNodeClick : function(node, e){
31681         this.select(node, e, e.ctrlKey);
31682     },
31683     
31684     /**
31685      * Select a node.
31686      * @param {TreeNode} node The node to select
31687      * @param {EventObject} e (optional) An event associated with the selection
31688      * @param {Boolean} keepExisting True to retain existing selections
31689      * @return {TreeNode} The selected node
31690      */
31691     select : function(node, e, keepExisting){
31692         if(keepExisting !== true){
31693             this.clearSelections(true);
31694         }
31695         if(this.isSelected(node)){
31696             this.lastSelNode = node;
31697             return node;
31698         }
31699         this.selNodes.push(node);
31700         this.selMap[node.id] = node;
31701         this.lastSelNode = node;
31702         node.ui.onSelectedChange(true);
31703         this.fireEvent("selectionchange", this, this.selNodes);
31704         return node;
31705     },
31706     
31707     /**
31708      * Deselect a node.
31709      * @param {TreeNode} node The node to unselect
31710      */
31711     unselect : function(node){
31712         if(this.selMap[node.id]){
31713             node.ui.onSelectedChange(false);
31714             var sn = this.selNodes;
31715             var index = -1;
31716             if(sn.indexOf){
31717                 index = sn.indexOf(node);
31718             }else{
31719                 for(var i = 0, len = sn.length; i < len; i++){
31720                     if(sn[i] == node){
31721                         index = i;
31722                         break;
31723                     }
31724                 }
31725             }
31726             if(index != -1){
31727                 this.selNodes.splice(index, 1);
31728             }
31729             delete this.selMap[node.id];
31730             this.fireEvent("selectionchange", this, this.selNodes);
31731         }
31732     },
31733     
31734     /**
31735      * Clear all selections
31736      */
31737     clearSelections : function(suppressEvent){
31738         var sn = this.selNodes;
31739         if(sn.length > 0){
31740             for(var i = 0, len = sn.length; i < len; i++){
31741                 sn[i].ui.onSelectedChange(false);
31742             }
31743             this.selNodes = [];
31744             this.selMap = {};
31745             if(suppressEvent !== true){
31746                 this.fireEvent("selectionchange", this, this.selNodes);
31747             }
31748         }
31749     },
31750     
31751     /**
31752      * Returns true if the node is selected
31753      * @param {TreeNode} node The node to check
31754      * @return {Boolean}
31755      */
31756     isSelected : function(node){
31757         return this.selMap[node.id] ? true : false;  
31758     },
31759     
31760     /**
31761      * Returns an array of the selected nodes
31762      * @return {Array}
31763      */
31764     getSelectedNodes : function(){
31765         return this.selNodes;    
31766     },
31767
31768     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31769
31770     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31771
31772     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31773 });/*
31774  * Based on:
31775  * Ext JS Library 1.1.1
31776  * Copyright(c) 2006-2007, Ext JS, LLC.
31777  *
31778  * Originally Released Under LGPL - original licence link has changed is not relivant.
31779  *
31780  * Fork - LGPL
31781  * <script type="text/javascript">
31782  */
31783  
31784 /**
31785  * @class Roo.tree.TreeNode
31786  * @extends Roo.data.Node
31787  * @cfg {String} text The text for this node
31788  * @cfg {Boolean} expanded true to start the node expanded
31789  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31790  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31791  * @cfg {Boolean} disabled true to start the node disabled
31792  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31793  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31794  * @cfg {String} cls A css class to be added to the node
31795  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31796  * @cfg {String} href URL of the link used for the node (defaults to #)
31797  * @cfg {String} hrefTarget target frame for the link
31798  * @cfg {String} qtip An Ext QuickTip for the node
31799  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31800  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31801  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31802  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31803  * (defaults to undefined with no checkbox rendered)
31804  * @constructor
31805  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31806  */
31807 Roo.tree.TreeNode = function(attributes){
31808     attributes = attributes || {};
31809     if(typeof attributes == "string"){
31810         attributes = {text: attributes};
31811     }
31812     this.childrenRendered = false;
31813     this.rendered = false;
31814     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31815     this.expanded = attributes.expanded === true;
31816     this.isTarget = attributes.isTarget !== false;
31817     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31818     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31819
31820     /**
31821      * Read-only. The text for this node. To change it use setText().
31822      * @type String
31823      */
31824     this.text = attributes.text;
31825     /**
31826      * True if this node is disabled.
31827      * @type Boolean
31828      */
31829     this.disabled = attributes.disabled === true;
31830
31831     this.addEvents({
31832         /**
31833         * @event textchange
31834         * Fires when the text for this node is changed
31835         * @param {Node} this This node
31836         * @param {String} text The new text
31837         * @param {String} oldText The old text
31838         */
31839         "textchange" : true,
31840         /**
31841         * @event beforeexpand
31842         * Fires before this node is expanded, return false to cancel.
31843         * @param {Node} this This node
31844         * @param {Boolean} deep
31845         * @param {Boolean} anim
31846         */
31847         "beforeexpand" : true,
31848         /**
31849         * @event beforecollapse
31850         * Fires before this node is collapsed, return false to cancel.
31851         * @param {Node} this This node
31852         * @param {Boolean} deep
31853         * @param {Boolean} anim
31854         */
31855         "beforecollapse" : true,
31856         /**
31857         * @event expand
31858         * Fires when this node is expanded
31859         * @param {Node} this This node
31860         */
31861         "expand" : true,
31862         /**
31863         * @event disabledchange
31864         * Fires when the disabled status of this node changes
31865         * @param {Node} this This node
31866         * @param {Boolean} disabled
31867         */
31868         "disabledchange" : true,
31869         /**
31870         * @event collapse
31871         * Fires when this node is collapsed
31872         * @param {Node} this This node
31873         */
31874         "collapse" : true,
31875         /**
31876         * @event beforeclick
31877         * Fires before click processing. Return false to cancel the default action.
31878         * @param {Node} this This node
31879         * @param {Roo.EventObject} e The event object
31880         */
31881         "beforeclick":true,
31882         /**
31883         * @event checkchange
31884         * Fires when a node with a checkbox's checked property changes
31885         * @param {Node} this This node
31886         * @param {Boolean} checked
31887         */
31888         "checkchange":true,
31889         /**
31890         * @event click
31891         * Fires when this node is clicked
31892         * @param {Node} this This node
31893         * @param {Roo.EventObject} e The event object
31894         */
31895         "click":true,
31896         /**
31897         * @event dblclick
31898         * Fires when this node is double clicked
31899         * @param {Node} this This node
31900         * @param {Roo.EventObject} e The event object
31901         */
31902         "dblclick":true,
31903         /**
31904         * @event contextmenu
31905         * Fires when this node is right clicked
31906         * @param {Node} this This node
31907         * @param {Roo.EventObject} e The event object
31908         */
31909         "contextmenu":true,
31910         /**
31911         * @event beforechildrenrendered
31912         * Fires right before the child nodes for this node are rendered
31913         * @param {Node} this This node
31914         */
31915         "beforechildrenrendered":true
31916     });
31917
31918     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31919
31920     /**
31921      * Read-only. The UI for this node
31922      * @type TreeNodeUI
31923      */
31924     this.ui = new uiClass(this);
31925     
31926     // finally support items[]
31927     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31928         return;
31929     }
31930     
31931     
31932     Roo.each(this.attributes.items, function(c) {
31933         this.appendChild(Roo.factory(c,Roo.Tree));
31934     }, this);
31935     delete this.attributes.items;
31936     
31937     
31938     
31939 };
31940 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31941     preventHScroll: true,
31942     /**
31943      * Returns true if this node is expanded
31944      * @return {Boolean}
31945      */
31946     isExpanded : function(){
31947         return this.expanded;
31948     },
31949
31950     /**
31951      * Returns the UI object for this node
31952      * @return {TreeNodeUI}
31953      */
31954     getUI : function(){
31955         return this.ui;
31956     },
31957
31958     // private override
31959     setFirstChild : function(node){
31960         var of = this.firstChild;
31961         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31962         if(this.childrenRendered && of && node != of){
31963             of.renderIndent(true, true);
31964         }
31965         if(this.rendered){
31966             this.renderIndent(true, true);
31967         }
31968     },
31969
31970     // private override
31971     setLastChild : function(node){
31972         var ol = this.lastChild;
31973         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31974         if(this.childrenRendered && ol && node != ol){
31975             ol.renderIndent(true, true);
31976         }
31977         if(this.rendered){
31978             this.renderIndent(true, true);
31979         }
31980     },
31981
31982     // these methods are overridden to provide lazy rendering support
31983     // private override
31984     appendChild : function()
31985     {
31986         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31987         if(node && this.childrenRendered){
31988             node.render();
31989         }
31990         this.ui.updateExpandIcon();
31991         return node;
31992     },
31993
31994     // private override
31995     removeChild : function(node){
31996         this.ownerTree.getSelectionModel().unselect(node);
31997         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31998         // if it's been rendered remove dom node
31999         if(this.childrenRendered){
32000             node.ui.remove();
32001         }
32002         if(this.childNodes.length < 1){
32003             this.collapse(false, false);
32004         }else{
32005             this.ui.updateExpandIcon();
32006         }
32007         if(!this.firstChild) {
32008             this.childrenRendered = false;
32009         }
32010         return node;
32011     },
32012
32013     // private override
32014     insertBefore : function(node, refNode){
32015         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32016         if(newNode && refNode && this.childrenRendered){
32017             node.render();
32018         }
32019         this.ui.updateExpandIcon();
32020         return newNode;
32021     },
32022
32023     /**
32024      * Sets the text for this node
32025      * @param {String} text
32026      */
32027     setText : function(text){
32028         var oldText = this.text;
32029         this.text = text;
32030         this.attributes.text = text;
32031         if(this.rendered){ // event without subscribing
32032             this.ui.onTextChange(this, text, oldText);
32033         }
32034         this.fireEvent("textchange", this, text, oldText);
32035     },
32036
32037     /**
32038      * Triggers selection of this node
32039      */
32040     select : function(){
32041         this.getOwnerTree().getSelectionModel().select(this);
32042     },
32043
32044     /**
32045      * Triggers deselection of this node
32046      */
32047     unselect : function(){
32048         this.getOwnerTree().getSelectionModel().unselect(this);
32049     },
32050
32051     /**
32052      * Returns true if this node is selected
32053      * @return {Boolean}
32054      */
32055     isSelected : function(){
32056         return this.getOwnerTree().getSelectionModel().isSelected(this);
32057     },
32058
32059     /**
32060      * Expand this node.
32061      * @param {Boolean} deep (optional) True to expand all children as well
32062      * @param {Boolean} anim (optional) false to cancel the default animation
32063      * @param {Function} callback (optional) A callback to be called when
32064      * expanding this node completes (does not wait for deep expand to complete).
32065      * Called with 1 parameter, this node.
32066      */
32067     expand : function(deep, anim, callback){
32068         if(!this.expanded){
32069             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32070                 return;
32071             }
32072             if(!this.childrenRendered){
32073                 this.renderChildren();
32074             }
32075             this.expanded = true;
32076             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32077                 this.ui.animExpand(function(){
32078                     this.fireEvent("expand", this);
32079                     if(typeof callback == "function"){
32080                         callback(this);
32081                     }
32082                     if(deep === true){
32083                         this.expandChildNodes(true);
32084                     }
32085                 }.createDelegate(this));
32086                 return;
32087             }else{
32088                 this.ui.expand();
32089                 this.fireEvent("expand", this);
32090                 if(typeof callback == "function"){
32091                     callback(this);
32092                 }
32093             }
32094         }else{
32095            if(typeof callback == "function"){
32096                callback(this);
32097            }
32098         }
32099         if(deep === true){
32100             this.expandChildNodes(true);
32101         }
32102     },
32103
32104     isHiddenRoot : function(){
32105         return this.isRoot && !this.getOwnerTree().rootVisible;
32106     },
32107
32108     /**
32109      * Collapse this node.
32110      * @param {Boolean} deep (optional) True to collapse all children as well
32111      * @param {Boolean} anim (optional) false to cancel the default animation
32112      */
32113     collapse : function(deep, anim){
32114         if(this.expanded && !this.isHiddenRoot()){
32115             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32116                 return;
32117             }
32118             this.expanded = false;
32119             if((this.getOwnerTree().animate && anim !== false) || anim){
32120                 this.ui.animCollapse(function(){
32121                     this.fireEvent("collapse", this);
32122                     if(deep === true){
32123                         this.collapseChildNodes(true);
32124                     }
32125                 }.createDelegate(this));
32126                 return;
32127             }else{
32128                 this.ui.collapse();
32129                 this.fireEvent("collapse", this);
32130             }
32131         }
32132         if(deep === true){
32133             var cs = this.childNodes;
32134             for(var i = 0, len = cs.length; i < len; i++) {
32135                 cs[i].collapse(true, false);
32136             }
32137         }
32138     },
32139
32140     // private
32141     delayedExpand : function(delay){
32142         if(!this.expandProcId){
32143             this.expandProcId = this.expand.defer(delay, this);
32144         }
32145     },
32146
32147     // private
32148     cancelExpand : function(){
32149         if(this.expandProcId){
32150             clearTimeout(this.expandProcId);
32151         }
32152         this.expandProcId = false;
32153     },
32154
32155     /**
32156      * Toggles expanded/collapsed state of the node
32157      */
32158     toggle : function(){
32159         if(this.expanded){
32160             this.collapse();
32161         }else{
32162             this.expand();
32163         }
32164     },
32165
32166     /**
32167      * Ensures all parent nodes are expanded
32168      */
32169     ensureVisible : function(callback){
32170         var tree = this.getOwnerTree();
32171         tree.expandPath(this.parentNode.getPath(), false, function(){
32172             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32173             Roo.callback(callback);
32174         }.createDelegate(this));
32175     },
32176
32177     /**
32178      * Expand all child nodes
32179      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32180      */
32181     expandChildNodes : function(deep){
32182         var cs = this.childNodes;
32183         for(var i = 0, len = cs.length; i < len; i++) {
32184                 cs[i].expand(deep);
32185         }
32186     },
32187
32188     /**
32189      * Collapse all child nodes
32190      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32191      */
32192     collapseChildNodes : function(deep){
32193         var cs = this.childNodes;
32194         for(var i = 0, len = cs.length; i < len; i++) {
32195                 cs[i].collapse(deep);
32196         }
32197     },
32198
32199     /**
32200      * Disables this node
32201      */
32202     disable : function(){
32203         this.disabled = true;
32204         this.unselect();
32205         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32206             this.ui.onDisableChange(this, true);
32207         }
32208         this.fireEvent("disabledchange", this, true);
32209     },
32210
32211     /**
32212      * Enables this node
32213      */
32214     enable : function(){
32215         this.disabled = false;
32216         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32217             this.ui.onDisableChange(this, false);
32218         }
32219         this.fireEvent("disabledchange", this, false);
32220     },
32221
32222     // private
32223     renderChildren : function(suppressEvent){
32224         if(suppressEvent !== false){
32225             this.fireEvent("beforechildrenrendered", this);
32226         }
32227         var cs = this.childNodes;
32228         for(var i = 0, len = cs.length; i < len; i++){
32229             cs[i].render(true);
32230         }
32231         this.childrenRendered = true;
32232     },
32233
32234     // private
32235     sort : function(fn, scope){
32236         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32237         if(this.childrenRendered){
32238             var cs = this.childNodes;
32239             for(var i = 0, len = cs.length; i < len; i++){
32240                 cs[i].render(true);
32241             }
32242         }
32243     },
32244
32245     // private
32246     render : function(bulkRender){
32247         this.ui.render(bulkRender);
32248         if(!this.rendered){
32249             this.rendered = true;
32250             if(this.expanded){
32251                 this.expanded = false;
32252                 this.expand(false, false);
32253             }
32254         }
32255     },
32256
32257     // private
32258     renderIndent : function(deep, refresh){
32259         if(refresh){
32260             this.ui.childIndent = null;
32261         }
32262         this.ui.renderIndent();
32263         if(deep === true && this.childrenRendered){
32264             var cs = this.childNodes;
32265             for(var i = 0, len = cs.length; i < len; i++){
32266                 cs[i].renderIndent(true, refresh);
32267             }
32268         }
32269     }
32270 });/*
32271  * Based on:
32272  * Ext JS Library 1.1.1
32273  * Copyright(c) 2006-2007, Ext JS, LLC.
32274  *
32275  * Originally Released Under LGPL - original licence link has changed is not relivant.
32276  *
32277  * Fork - LGPL
32278  * <script type="text/javascript">
32279  */
32280  
32281 /**
32282  * @class Roo.tree.AsyncTreeNode
32283  * @extends Roo.tree.TreeNode
32284  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32285  * @constructor
32286  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32287  */
32288  Roo.tree.AsyncTreeNode = function(config){
32289     this.loaded = false;
32290     this.loading = false;
32291     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32292     /**
32293     * @event beforeload
32294     * Fires before this node is loaded, return false to cancel
32295     * @param {Node} this This node
32296     */
32297     this.addEvents({'beforeload':true, 'load': true});
32298     /**
32299     * @event load
32300     * Fires when this node is loaded
32301     * @param {Node} this This node
32302     */
32303     /**
32304      * The loader used by this node (defaults to using the tree's defined loader)
32305      * @type TreeLoader
32306      * @property loader
32307      */
32308 };
32309 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32310     expand : function(deep, anim, callback){
32311         if(this.loading){ // if an async load is already running, waiting til it's done
32312             var timer;
32313             var f = function(){
32314                 if(!this.loading){ // done loading
32315                     clearInterval(timer);
32316                     this.expand(deep, anim, callback);
32317                 }
32318             }.createDelegate(this);
32319             timer = setInterval(f, 200);
32320             return;
32321         }
32322         if(!this.loaded){
32323             if(this.fireEvent("beforeload", this) === false){
32324                 return;
32325             }
32326             this.loading = true;
32327             this.ui.beforeLoad(this);
32328             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32329             if(loader){
32330                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32331                 return;
32332             }
32333         }
32334         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32335     },
32336     
32337     /**
32338      * Returns true if this node is currently loading
32339      * @return {Boolean}
32340      */
32341     isLoading : function(){
32342         return this.loading;  
32343     },
32344     
32345     loadComplete : function(deep, anim, callback){
32346         this.loading = false;
32347         this.loaded = true;
32348         this.ui.afterLoad(this);
32349         this.fireEvent("load", this);
32350         this.expand(deep, anim, callback);
32351     },
32352     
32353     /**
32354      * Returns true if this node has been loaded
32355      * @return {Boolean}
32356      */
32357     isLoaded : function(){
32358         return this.loaded;
32359     },
32360     
32361     hasChildNodes : function(){
32362         if(!this.isLeaf() && !this.loaded){
32363             return true;
32364         }else{
32365             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32366         }
32367     },
32368
32369     /**
32370      * Trigger a reload for this node
32371      * @param {Function} callback
32372      */
32373     reload : function(callback){
32374         this.collapse(false, false);
32375         while(this.firstChild){
32376             this.removeChild(this.firstChild);
32377         }
32378         this.childrenRendered = false;
32379         this.loaded = false;
32380         if(this.isHiddenRoot()){
32381             this.expanded = false;
32382         }
32383         this.expand(false, false, callback);
32384     }
32385 });/*
32386  * Based on:
32387  * Ext JS Library 1.1.1
32388  * Copyright(c) 2006-2007, Ext JS, LLC.
32389  *
32390  * Originally Released Under LGPL - original licence link has changed is not relivant.
32391  *
32392  * Fork - LGPL
32393  * <script type="text/javascript">
32394  */
32395  
32396 /**
32397  * @class Roo.tree.TreeNodeUI
32398  * @constructor
32399  * @param {Object} node The node to render
32400  * The TreeNode UI implementation is separate from the
32401  * tree implementation. Unless you are customizing the tree UI,
32402  * you should never have to use this directly.
32403  */
32404 Roo.tree.TreeNodeUI = function(node){
32405     this.node = node;
32406     this.rendered = false;
32407     this.animating = false;
32408     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32409 };
32410
32411 Roo.tree.TreeNodeUI.prototype = {
32412     removeChild : function(node){
32413         if(this.rendered){
32414             this.ctNode.removeChild(node.ui.getEl());
32415         }
32416     },
32417
32418     beforeLoad : function(){
32419          this.addClass("x-tree-node-loading");
32420     },
32421
32422     afterLoad : function(){
32423          this.removeClass("x-tree-node-loading");
32424     },
32425
32426     onTextChange : function(node, text, oldText){
32427         if(this.rendered){
32428             this.textNode.innerHTML = text;
32429         }
32430     },
32431
32432     onDisableChange : function(node, state){
32433         this.disabled = state;
32434         if(state){
32435             this.addClass("x-tree-node-disabled");
32436         }else{
32437             this.removeClass("x-tree-node-disabled");
32438         }
32439     },
32440
32441     onSelectedChange : function(state){
32442         if(state){
32443             this.focus();
32444             this.addClass("x-tree-selected");
32445         }else{
32446             //this.blur();
32447             this.removeClass("x-tree-selected");
32448         }
32449     },
32450
32451     onMove : function(tree, node, oldParent, newParent, index, refNode){
32452         this.childIndent = null;
32453         if(this.rendered){
32454             var targetNode = newParent.ui.getContainer();
32455             if(!targetNode){//target not rendered
32456                 this.holder = document.createElement("div");
32457                 this.holder.appendChild(this.wrap);
32458                 return;
32459             }
32460             var insertBefore = refNode ? refNode.ui.getEl() : null;
32461             if(insertBefore){
32462                 targetNode.insertBefore(this.wrap, insertBefore);
32463             }else{
32464                 targetNode.appendChild(this.wrap);
32465             }
32466             this.node.renderIndent(true);
32467         }
32468     },
32469
32470     addClass : function(cls){
32471         if(this.elNode){
32472             Roo.fly(this.elNode).addClass(cls);
32473         }
32474     },
32475
32476     removeClass : function(cls){
32477         if(this.elNode){
32478             Roo.fly(this.elNode).removeClass(cls);
32479         }
32480     },
32481
32482     remove : function(){
32483         if(this.rendered){
32484             this.holder = document.createElement("div");
32485             this.holder.appendChild(this.wrap);
32486         }
32487     },
32488
32489     fireEvent : function(){
32490         return this.node.fireEvent.apply(this.node, arguments);
32491     },
32492
32493     initEvents : function(){
32494         this.node.on("move", this.onMove, this);
32495         var E = Roo.EventManager;
32496         var a = this.anchor;
32497
32498         var el = Roo.fly(a, '_treeui');
32499
32500         if(Roo.isOpera){ // opera render bug ignores the CSS
32501             el.setStyle("text-decoration", "none");
32502         }
32503
32504         el.on("click", this.onClick, this);
32505         el.on("dblclick", this.onDblClick, this);
32506
32507         if(this.checkbox){
32508             Roo.EventManager.on(this.checkbox,
32509                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32510         }
32511
32512         el.on("contextmenu", this.onContextMenu, this);
32513
32514         var icon = Roo.fly(this.iconNode);
32515         icon.on("click", this.onClick, this);
32516         icon.on("dblclick", this.onDblClick, this);
32517         icon.on("contextmenu", this.onContextMenu, this);
32518         E.on(this.ecNode, "click", this.ecClick, this, true);
32519
32520         if(this.node.disabled){
32521             this.addClass("x-tree-node-disabled");
32522         }
32523         if(this.node.hidden){
32524             this.addClass("x-tree-node-disabled");
32525         }
32526         var ot = this.node.getOwnerTree();
32527         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32528         if(dd && (!this.node.isRoot || ot.rootVisible)){
32529             Roo.dd.Registry.register(this.elNode, {
32530                 node: this.node,
32531                 handles: this.getDDHandles(),
32532                 isHandle: false
32533             });
32534         }
32535     },
32536
32537     getDDHandles : function(){
32538         return [this.iconNode, this.textNode];
32539     },
32540
32541     hide : function(){
32542         if(this.rendered){
32543             this.wrap.style.display = "none";
32544         }
32545     },
32546
32547     show : function(){
32548         if(this.rendered){
32549             this.wrap.style.display = "";
32550         }
32551     },
32552
32553     onContextMenu : function(e){
32554         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32555             e.preventDefault();
32556             this.focus();
32557             this.fireEvent("contextmenu", this.node, e);
32558         }
32559     },
32560
32561     onClick : function(e){
32562         if(this.dropping){
32563             e.stopEvent();
32564             return;
32565         }
32566         if(this.fireEvent("beforeclick", this.node, e) !== false){
32567             if(!this.disabled && this.node.attributes.href){
32568                 this.fireEvent("click", this.node, e);
32569                 return;
32570             }
32571             e.preventDefault();
32572             if(this.disabled){
32573                 return;
32574             }
32575
32576             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32577                 this.node.toggle();
32578             }
32579
32580             this.fireEvent("click", this.node, e);
32581         }else{
32582             e.stopEvent();
32583         }
32584     },
32585
32586     onDblClick : function(e){
32587         e.preventDefault();
32588         if(this.disabled){
32589             return;
32590         }
32591         if(this.checkbox){
32592             this.toggleCheck();
32593         }
32594         if(!this.animating && this.node.hasChildNodes()){
32595             this.node.toggle();
32596         }
32597         this.fireEvent("dblclick", this.node, e);
32598     },
32599
32600     onCheckChange : function(){
32601         var checked = this.checkbox.checked;
32602         this.node.attributes.checked = checked;
32603         this.fireEvent('checkchange', this.node, checked);
32604     },
32605
32606     ecClick : function(e){
32607         if(!this.animating && this.node.hasChildNodes()){
32608             this.node.toggle();
32609         }
32610     },
32611
32612     startDrop : function(){
32613         this.dropping = true;
32614     },
32615
32616     // delayed drop so the click event doesn't get fired on a drop
32617     endDrop : function(){
32618        setTimeout(function(){
32619            this.dropping = false;
32620        }.createDelegate(this), 50);
32621     },
32622
32623     expand : function(){
32624         this.updateExpandIcon();
32625         this.ctNode.style.display = "";
32626     },
32627
32628     focus : function(){
32629         if(!this.node.preventHScroll){
32630             try{this.anchor.focus();
32631             }catch(e){}
32632         }else if(!Roo.isIE){
32633             try{
32634                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32635                 var l = noscroll.scrollLeft;
32636                 this.anchor.focus();
32637                 noscroll.scrollLeft = l;
32638             }catch(e){}
32639         }
32640     },
32641
32642     toggleCheck : function(value){
32643         var cb = this.checkbox;
32644         if(cb){
32645             cb.checked = (value === undefined ? !cb.checked : value);
32646         }
32647     },
32648
32649     blur : function(){
32650         try{
32651             this.anchor.blur();
32652         }catch(e){}
32653     },
32654
32655     animExpand : function(callback){
32656         var ct = Roo.get(this.ctNode);
32657         ct.stopFx();
32658         if(!this.node.hasChildNodes()){
32659             this.updateExpandIcon();
32660             this.ctNode.style.display = "";
32661             Roo.callback(callback);
32662             return;
32663         }
32664         this.animating = true;
32665         this.updateExpandIcon();
32666
32667         ct.slideIn('t', {
32668            callback : function(){
32669                this.animating = false;
32670                Roo.callback(callback);
32671             },
32672             scope: this,
32673             duration: this.node.ownerTree.duration || .25
32674         });
32675     },
32676
32677     highlight : function(){
32678         var tree = this.node.getOwnerTree();
32679         Roo.fly(this.wrap).highlight(
32680             tree.hlColor || "C3DAF9",
32681             {endColor: tree.hlBaseColor}
32682         );
32683     },
32684
32685     collapse : function(){
32686         this.updateExpandIcon();
32687         this.ctNode.style.display = "none";
32688     },
32689
32690     animCollapse : function(callback){
32691         var ct = Roo.get(this.ctNode);
32692         ct.enableDisplayMode('block');
32693         ct.stopFx();
32694
32695         this.animating = true;
32696         this.updateExpandIcon();
32697
32698         ct.slideOut('t', {
32699             callback : function(){
32700                this.animating = false;
32701                Roo.callback(callback);
32702             },
32703             scope: this,
32704             duration: this.node.ownerTree.duration || .25
32705         });
32706     },
32707
32708     getContainer : function(){
32709         return this.ctNode;
32710     },
32711
32712     getEl : function(){
32713         return this.wrap;
32714     },
32715
32716     appendDDGhost : function(ghostNode){
32717         ghostNode.appendChild(this.elNode.cloneNode(true));
32718     },
32719
32720     getDDRepairXY : function(){
32721         return Roo.lib.Dom.getXY(this.iconNode);
32722     },
32723
32724     onRender : function(){
32725         this.render();
32726     },
32727
32728     render : function(bulkRender){
32729         var n = this.node, a = n.attributes;
32730         var targetNode = n.parentNode ?
32731               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32732
32733         if(!this.rendered){
32734             this.rendered = true;
32735
32736             this.renderElements(n, a, targetNode, bulkRender);
32737
32738             if(a.qtip){
32739                if(this.textNode.setAttributeNS){
32740                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32741                    if(a.qtipTitle){
32742                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32743                    }
32744                }else{
32745                    this.textNode.setAttribute("ext:qtip", a.qtip);
32746                    if(a.qtipTitle){
32747                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32748                    }
32749                }
32750             }else if(a.qtipCfg){
32751                 a.qtipCfg.target = Roo.id(this.textNode);
32752                 Roo.QuickTips.register(a.qtipCfg);
32753             }
32754             this.initEvents();
32755             if(!this.node.expanded){
32756                 this.updateExpandIcon();
32757             }
32758         }else{
32759             if(bulkRender === true) {
32760                 targetNode.appendChild(this.wrap);
32761             }
32762         }
32763     },
32764
32765     renderElements : function(n, a, targetNode, bulkRender)
32766     {
32767         // add some indent caching, this helps performance when rendering a large tree
32768         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32769         var t = n.getOwnerTree();
32770         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32771         if (typeof(n.attributes.html) != 'undefined') {
32772             txt = n.attributes.html;
32773         }
32774         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32775         var cb = typeof a.checked == 'boolean';
32776         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32777         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32778             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32779             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32780             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32781             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32782             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32783              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32784                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32785             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32786             "</li>"];
32787
32788         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32789             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32790                                 n.nextSibling.ui.getEl(), buf.join(""));
32791         }else{
32792             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32793         }
32794
32795         this.elNode = this.wrap.childNodes[0];
32796         this.ctNode = this.wrap.childNodes[1];
32797         var cs = this.elNode.childNodes;
32798         this.indentNode = cs[0];
32799         this.ecNode = cs[1];
32800         this.iconNode = cs[2];
32801         var index = 3;
32802         if(cb){
32803             this.checkbox = cs[3];
32804             index++;
32805         }
32806         this.anchor = cs[index];
32807         this.textNode = cs[index].firstChild;
32808     },
32809
32810     getAnchor : function(){
32811         return this.anchor;
32812     },
32813
32814     getTextEl : function(){
32815         return this.textNode;
32816     },
32817
32818     getIconEl : function(){
32819         return this.iconNode;
32820     },
32821
32822     isChecked : function(){
32823         return this.checkbox ? this.checkbox.checked : false;
32824     },
32825
32826     updateExpandIcon : function(){
32827         if(this.rendered){
32828             var n = this.node, c1, c2;
32829             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32830             var hasChild = n.hasChildNodes();
32831             if(hasChild){
32832                 if(n.expanded){
32833                     cls += "-minus";
32834                     c1 = "x-tree-node-collapsed";
32835                     c2 = "x-tree-node-expanded";
32836                 }else{
32837                     cls += "-plus";
32838                     c1 = "x-tree-node-expanded";
32839                     c2 = "x-tree-node-collapsed";
32840                 }
32841                 if(this.wasLeaf){
32842                     this.removeClass("x-tree-node-leaf");
32843                     this.wasLeaf = false;
32844                 }
32845                 if(this.c1 != c1 || this.c2 != c2){
32846                     Roo.fly(this.elNode).replaceClass(c1, c2);
32847                     this.c1 = c1; this.c2 = c2;
32848                 }
32849             }else{
32850                 // this changes non-leafs into leafs if they have no children.
32851                 // it's not very rational behaviour..
32852                 
32853                 if(!this.wasLeaf && this.node.leaf){
32854                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32855                     delete this.c1;
32856                     delete this.c2;
32857                     this.wasLeaf = true;
32858                 }
32859             }
32860             var ecc = "x-tree-ec-icon "+cls;
32861             if(this.ecc != ecc){
32862                 this.ecNode.className = ecc;
32863                 this.ecc = ecc;
32864             }
32865         }
32866     },
32867
32868     getChildIndent : function(){
32869         if(!this.childIndent){
32870             var buf = [];
32871             var p = this.node;
32872             while(p){
32873                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32874                     if(!p.isLast()) {
32875                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32876                     } else {
32877                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32878                     }
32879                 }
32880                 p = p.parentNode;
32881             }
32882             this.childIndent = buf.join("");
32883         }
32884         return this.childIndent;
32885     },
32886
32887     renderIndent : function(){
32888         if(this.rendered){
32889             var indent = "";
32890             var p = this.node.parentNode;
32891             if(p){
32892                 indent = p.ui.getChildIndent();
32893             }
32894             if(this.indentMarkup != indent){ // don't rerender if not required
32895                 this.indentNode.innerHTML = indent;
32896                 this.indentMarkup = indent;
32897             }
32898             this.updateExpandIcon();
32899         }
32900     }
32901 };
32902
32903 Roo.tree.RootTreeNodeUI = function(){
32904     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32905 };
32906 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32907     render : function(){
32908         if(!this.rendered){
32909             var targetNode = this.node.ownerTree.innerCt.dom;
32910             this.node.expanded = true;
32911             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32912             this.wrap = this.ctNode = targetNode.firstChild;
32913         }
32914     },
32915     collapse : function(){
32916     },
32917     expand : function(){
32918     }
32919 });/*
32920  * Based on:
32921  * Ext JS Library 1.1.1
32922  * Copyright(c) 2006-2007, Ext JS, LLC.
32923  *
32924  * Originally Released Under LGPL - original licence link has changed is not relivant.
32925  *
32926  * Fork - LGPL
32927  * <script type="text/javascript">
32928  */
32929 /**
32930  * @class Roo.tree.TreeLoader
32931  * @extends Roo.util.Observable
32932  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32933  * nodes from a specified URL. The response must be a javascript Array definition
32934  * who's elements are node definition objects. eg:
32935  * <pre><code>
32936 {  success : true,
32937    data :      [
32938    
32939     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
32940     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
32941     ]
32942 }
32943
32944
32945 </code></pre>
32946  * <br><br>
32947  * The old style respose with just an array is still supported, but not recommended.
32948  * <br><br>
32949  *
32950  * A server request is sent, and child nodes are loaded only when a node is expanded.
32951  * The loading node's id is passed to the server under the parameter name "node" to
32952  * enable the server to produce the correct child nodes.
32953  * <br><br>
32954  * To pass extra parameters, an event handler may be attached to the "beforeload"
32955  * event, and the parameters specified in the TreeLoader's baseParams property:
32956  * <pre><code>
32957     myTreeLoader.on("beforeload", function(treeLoader, node) {
32958         this.baseParams.category = node.attributes.category;
32959     }, this);
32960 </code></pre><
32961  * This would pass an HTTP parameter called "category" to the server containing
32962  * the value of the Node's "category" attribute.
32963  * @constructor
32964  * Creates a new Treeloader.
32965  * @param {Object} config A config object containing config properties.
32966  */
32967 Roo.tree.TreeLoader = function(config){
32968     this.baseParams = {};
32969     this.requestMethod = "POST";
32970     Roo.apply(this, config);
32971
32972     this.addEvents({
32973     
32974         /**
32975          * @event beforeload
32976          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32977          * @param {Object} This TreeLoader object.
32978          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32979          * @param {Object} callback The callback function specified in the {@link #load} call.
32980          */
32981         beforeload : true,
32982         /**
32983          * @event load
32984          * Fires when the node has been successfuly loaded.
32985          * @param {Object} This TreeLoader object.
32986          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32987          * @param {Object} response The response object containing the data from the server.
32988          */
32989         load : true,
32990         /**
32991          * @event loadexception
32992          * Fires if the network request failed.
32993          * @param {Object} This TreeLoader object.
32994          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32995          * @param {Object} response The response object containing the data from the server.
32996          */
32997         loadexception : true,
32998         /**
32999          * @event create
33000          * Fires before a node is created, enabling you to return custom Node types 
33001          * @param {Object} This TreeLoader object.
33002          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33003          */
33004         create : true
33005     });
33006
33007     Roo.tree.TreeLoader.superclass.constructor.call(this);
33008 };
33009
33010 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33011     /**
33012     * @cfg {String} dataUrl The URL from which to request a Json string which
33013     * specifies an array of node definition object representing the child nodes
33014     * to be loaded.
33015     */
33016     /**
33017     * @cfg {Object} baseParams (optional) An object containing properties which
33018     * specify HTTP parameters to be passed to each request for child nodes.
33019     */
33020     /**
33021     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33022     * created by this loader. If the attributes sent by the server have an attribute in this object,
33023     * they take priority.
33024     */
33025     /**
33026     * @cfg {Object} uiProviders (optional) An object containing properties which
33027     * 
33028     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33029     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33030     * <i>uiProvider</i> attribute of a returned child node is a string rather
33031     * than a reference to a TreeNodeUI implementation, this that string value
33032     * is used as a property name in the uiProviders object. You can define the provider named
33033     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33034     */
33035     uiProviders : {},
33036
33037     /**
33038     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33039     * child nodes before loading.
33040     */
33041     clearOnLoad : true,
33042
33043     /**
33044     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33045     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33046     * Grid query { data : [ .....] }
33047     */
33048     
33049     root : false,
33050      /**
33051     * @cfg {String} queryParam (optional) 
33052     * Name of the query as it will be passed on the querystring (defaults to 'node')
33053     * eg. the request will be ?node=[id]
33054     */
33055     
33056     
33057     queryParam: false,
33058     
33059     /**
33060      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33061      * This is called automatically when a node is expanded, but may be used to reload
33062      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33063      * @param {Roo.tree.TreeNode} node
33064      * @param {Function} callback
33065      */
33066     load : function(node, callback){
33067         if(this.clearOnLoad){
33068             while(node.firstChild){
33069                 node.removeChild(node.firstChild);
33070             }
33071         }
33072         if(node.attributes.children){ // preloaded json children
33073             var cs = node.attributes.children;
33074             for(var i = 0, len = cs.length; i < len; i++){
33075                 node.appendChild(this.createNode(cs[i]));
33076             }
33077             if(typeof callback == "function"){
33078                 callback();
33079             }
33080         }else if(this.dataUrl){
33081             this.requestData(node, callback);
33082         }
33083     },
33084
33085     getParams: function(node){
33086         var buf = [], bp = this.baseParams;
33087         for(var key in bp){
33088             if(typeof bp[key] != "function"){
33089                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33090             }
33091         }
33092         var n = this.queryParam === false ? 'node' : this.queryParam;
33093         buf.push(n + "=", encodeURIComponent(node.id));
33094         return buf.join("");
33095     },
33096
33097     requestData : function(node, callback){
33098         if(this.fireEvent("beforeload", this, node, callback) !== false){
33099             this.transId = Roo.Ajax.request({
33100                 method:this.requestMethod,
33101                 url: this.dataUrl||this.url,
33102                 success: this.handleResponse,
33103                 failure: this.handleFailure,
33104                 scope: this,
33105                 argument: {callback: callback, node: node},
33106                 params: this.getParams(node)
33107             });
33108         }else{
33109             // if the load is cancelled, make sure we notify
33110             // the node that we are done
33111             if(typeof callback == "function"){
33112                 callback();
33113             }
33114         }
33115     },
33116
33117     isLoading : function(){
33118         return this.transId ? true : false;
33119     },
33120
33121     abort : function(){
33122         if(this.isLoading()){
33123             Roo.Ajax.abort(this.transId);
33124         }
33125     },
33126
33127     // private
33128     createNode : function(attr)
33129     {
33130         // apply baseAttrs, nice idea Corey!
33131         if(this.baseAttrs){
33132             Roo.applyIf(attr, this.baseAttrs);
33133         }
33134         if(this.applyLoader !== false){
33135             attr.loader = this;
33136         }
33137         // uiProvider = depreciated..
33138         
33139         if(typeof(attr.uiProvider) == 'string'){
33140            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33141                 /**  eval:var:attr */ eval(attr.uiProvider);
33142         }
33143         if(typeof(this.uiProviders['default']) != 'undefined') {
33144             attr.uiProvider = this.uiProviders['default'];
33145         }
33146         
33147         this.fireEvent('create', this, attr);
33148         
33149         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33150         return(attr.leaf ?
33151                         new Roo.tree.TreeNode(attr) :
33152                         new Roo.tree.AsyncTreeNode(attr));
33153     },
33154
33155     processResponse : function(response, node, callback)
33156     {
33157         var json = response.responseText;
33158         try {
33159             
33160             var o = Roo.decode(json);
33161             
33162             if (this.root === false && typeof(o.success) != undefined) {
33163                 this.root = 'data'; // the default behaviour for list like data..
33164                 }
33165                 
33166             if (this.root !== false &&  !o.success) {
33167                 // it's a failure condition.
33168                 var a = response.argument;
33169                 this.fireEvent("loadexception", this, a.node, response);
33170                 Roo.log("Load failed - should have a handler really");
33171                 return;
33172             }
33173             
33174             
33175             
33176             if (this.root !== false) {
33177                  o = o[this.root];
33178             }
33179             
33180             for(var i = 0, len = o.length; i < len; i++){
33181                 var n = this.createNode(o[i]);
33182                 if(n){
33183                     node.appendChild(n);
33184                 }
33185             }
33186             if(typeof callback == "function"){
33187                 callback(this, node);
33188             }
33189         }catch(e){
33190             this.handleFailure(response);
33191         }
33192     },
33193
33194     handleResponse : function(response){
33195         this.transId = false;
33196         var a = response.argument;
33197         this.processResponse(response, a.node, a.callback);
33198         this.fireEvent("load", this, a.node, response);
33199     },
33200
33201     handleFailure : function(response)
33202     {
33203         // should handle failure better..
33204         this.transId = false;
33205         var a = response.argument;
33206         this.fireEvent("loadexception", this, a.node, response);
33207         if(typeof a.callback == "function"){
33208             a.callback(this, a.node);
33209         }
33210     }
33211 });/*
33212  * Based on:
33213  * Ext JS Library 1.1.1
33214  * Copyright(c) 2006-2007, Ext JS, LLC.
33215  *
33216  * Originally Released Under LGPL - original licence link has changed is not relivant.
33217  *
33218  * Fork - LGPL
33219  * <script type="text/javascript">
33220  */
33221
33222 /**
33223 * @class Roo.tree.TreeFilter
33224 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33225 * @param {TreePanel} tree
33226 * @param {Object} config (optional)
33227  */
33228 Roo.tree.TreeFilter = function(tree, config){
33229     this.tree = tree;
33230     this.filtered = {};
33231     Roo.apply(this, config);
33232 };
33233
33234 Roo.tree.TreeFilter.prototype = {
33235     clearBlank:false,
33236     reverse:false,
33237     autoClear:false,
33238     remove:false,
33239
33240      /**
33241      * Filter the data by a specific attribute.
33242      * @param {String/RegExp} value Either string that the attribute value
33243      * should start with or a RegExp to test against the attribute
33244      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33245      * @param {TreeNode} startNode (optional) The node to start the filter at.
33246      */
33247     filter : function(value, attr, startNode){
33248         attr = attr || "text";
33249         var f;
33250         if(typeof value == "string"){
33251             var vlen = value.length;
33252             // auto clear empty filter
33253             if(vlen == 0 && this.clearBlank){
33254                 this.clear();
33255                 return;
33256             }
33257             value = value.toLowerCase();
33258             f = function(n){
33259                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33260             };
33261         }else if(value.exec){ // regex?
33262             f = function(n){
33263                 return value.test(n.attributes[attr]);
33264             };
33265         }else{
33266             throw 'Illegal filter type, must be string or regex';
33267         }
33268         this.filterBy(f, null, startNode);
33269         },
33270
33271     /**
33272      * Filter by a function. The passed function will be called with each
33273      * node in the tree (or from the startNode). If the function returns true, the node is kept
33274      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33275      * @param {Function} fn The filter function
33276      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33277      */
33278     filterBy : function(fn, scope, startNode){
33279         startNode = startNode || this.tree.root;
33280         if(this.autoClear){
33281             this.clear();
33282         }
33283         var af = this.filtered, rv = this.reverse;
33284         var f = function(n){
33285             if(n == startNode){
33286                 return true;
33287             }
33288             if(af[n.id]){
33289                 return false;
33290             }
33291             var m = fn.call(scope || n, n);
33292             if(!m || rv){
33293                 af[n.id] = n;
33294                 n.ui.hide();
33295                 return false;
33296             }
33297             return true;
33298         };
33299         startNode.cascade(f);
33300         if(this.remove){
33301            for(var id in af){
33302                if(typeof id != "function"){
33303                    var n = af[id];
33304                    if(n && n.parentNode){
33305                        n.parentNode.removeChild(n);
33306                    }
33307                }
33308            }
33309         }
33310     },
33311
33312     /**
33313      * Clears the current filter. Note: with the "remove" option
33314      * set a filter cannot be cleared.
33315      */
33316     clear : function(){
33317         var t = this.tree;
33318         var af = this.filtered;
33319         for(var id in af){
33320             if(typeof id != "function"){
33321                 var n = af[id];
33322                 if(n){
33323                     n.ui.show();
33324                 }
33325             }
33326         }
33327         this.filtered = {};
33328     }
33329 };
33330 /*
33331  * Based on:
33332  * Ext JS Library 1.1.1
33333  * Copyright(c) 2006-2007, Ext JS, LLC.
33334  *
33335  * Originally Released Under LGPL - original licence link has changed is not relivant.
33336  *
33337  * Fork - LGPL
33338  * <script type="text/javascript">
33339  */
33340  
33341
33342 /**
33343  * @class Roo.tree.TreeSorter
33344  * Provides sorting of nodes in a TreePanel
33345  * 
33346  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33347  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33348  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33349  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33350  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33351  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33352  * @constructor
33353  * @param {TreePanel} tree
33354  * @param {Object} config
33355  */
33356 Roo.tree.TreeSorter = function(tree, config){
33357     Roo.apply(this, config);
33358     tree.on("beforechildrenrendered", this.doSort, this);
33359     tree.on("append", this.updateSort, this);
33360     tree.on("insert", this.updateSort, this);
33361     
33362     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33363     var p = this.property || "text";
33364     var sortType = this.sortType;
33365     var fs = this.folderSort;
33366     var cs = this.caseSensitive === true;
33367     var leafAttr = this.leafAttr || 'leaf';
33368
33369     this.sortFn = function(n1, n2){
33370         if(fs){
33371             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33372                 return 1;
33373             }
33374             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33375                 return -1;
33376             }
33377         }
33378         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33379         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33380         if(v1 < v2){
33381                         return dsc ? +1 : -1;
33382                 }else if(v1 > v2){
33383                         return dsc ? -1 : +1;
33384         }else{
33385                 return 0;
33386         }
33387     };
33388 };
33389
33390 Roo.tree.TreeSorter.prototype = {
33391     doSort : function(node){
33392         node.sort(this.sortFn);
33393     },
33394     
33395     compareNodes : function(n1, n2){
33396         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33397     },
33398     
33399     updateSort : function(tree, node){
33400         if(node.childrenRendered){
33401             this.doSort.defer(1, this, [node]);
33402         }
33403     }
33404 };/*
33405  * Based on:
33406  * Ext JS Library 1.1.1
33407  * Copyright(c) 2006-2007, Ext JS, LLC.
33408  *
33409  * Originally Released Under LGPL - original licence link has changed is not relivant.
33410  *
33411  * Fork - LGPL
33412  * <script type="text/javascript">
33413  */
33414
33415 if(Roo.dd.DropZone){
33416     
33417 Roo.tree.TreeDropZone = function(tree, config){
33418     this.allowParentInsert = false;
33419     this.allowContainerDrop = false;
33420     this.appendOnly = false;
33421     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33422     this.tree = tree;
33423     this.lastInsertClass = "x-tree-no-status";
33424     this.dragOverData = {};
33425 };
33426
33427 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33428     ddGroup : "TreeDD",
33429     
33430     expandDelay : 1000,
33431     
33432     expandNode : function(node){
33433         if(node.hasChildNodes() && !node.isExpanded()){
33434             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33435         }
33436     },
33437     
33438     queueExpand : function(node){
33439         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33440     },
33441     
33442     cancelExpand : function(){
33443         if(this.expandProcId){
33444             clearTimeout(this.expandProcId);
33445             this.expandProcId = false;
33446         }
33447     },
33448     
33449     isValidDropPoint : function(n, pt, dd, e, data){
33450         if(!n || !data){ return false; }
33451         var targetNode = n.node;
33452         var dropNode = data.node;
33453         // default drop rules
33454         if(!(targetNode && targetNode.isTarget && pt)){
33455             return false;
33456         }
33457         if(pt == "append" && targetNode.allowChildren === false){
33458             return false;
33459         }
33460         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33461             return false;
33462         }
33463         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33464             return false;
33465         }
33466         // reuse the object
33467         var overEvent = this.dragOverData;
33468         overEvent.tree = this.tree;
33469         overEvent.target = targetNode;
33470         overEvent.data = data;
33471         overEvent.point = pt;
33472         overEvent.source = dd;
33473         overEvent.rawEvent = e;
33474         overEvent.dropNode = dropNode;
33475         overEvent.cancel = false;  
33476         var result = this.tree.fireEvent("nodedragover", overEvent);
33477         return overEvent.cancel === false && result !== false;
33478     },
33479     
33480     getDropPoint : function(e, n, dd){
33481         var tn = n.node;
33482         if(tn.isRoot){
33483             return tn.allowChildren !== false ? "append" : false; // always append for root
33484         }
33485         var dragEl = n.ddel;
33486         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33487         var y = Roo.lib.Event.getPageY(e);
33488         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33489         
33490         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33491         var noAppend = tn.allowChildren === false;
33492         if(this.appendOnly || tn.parentNode.allowChildren === false){
33493             return noAppend ? false : "append";
33494         }
33495         var noBelow = false;
33496         if(!this.allowParentInsert){
33497             noBelow = tn.hasChildNodes() && tn.isExpanded();
33498         }
33499         var q = (b - t) / (noAppend ? 2 : 3);
33500         if(y >= t && y < (t + q)){
33501             return "above";
33502         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33503             return "below";
33504         }else{
33505             return "append";
33506         }
33507     },
33508     
33509     onNodeEnter : function(n, dd, e, data){
33510         this.cancelExpand();
33511     },
33512     
33513     onNodeOver : function(n, dd, e, data){
33514         var pt = this.getDropPoint(e, n, dd);
33515         var node = n.node;
33516         
33517         // auto node expand check
33518         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33519             this.queueExpand(node);
33520         }else if(pt != "append"){
33521             this.cancelExpand();
33522         }
33523         
33524         // set the insert point style on the target node
33525         var returnCls = this.dropNotAllowed;
33526         if(this.isValidDropPoint(n, pt, dd, e, data)){
33527            if(pt){
33528                var el = n.ddel;
33529                var cls;
33530                if(pt == "above"){
33531                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33532                    cls = "x-tree-drag-insert-above";
33533                }else if(pt == "below"){
33534                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33535                    cls = "x-tree-drag-insert-below";
33536                }else{
33537                    returnCls = "x-tree-drop-ok-append";
33538                    cls = "x-tree-drag-append";
33539                }
33540                if(this.lastInsertClass != cls){
33541                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33542                    this.lastInsertClass = cls;
33543                }
33544            }
33545        }
33546        return returnCls;
33547     },
33548     
33549     onNodeOut : function(n, dd, e, data){
33550         this.cancelExpand();
33551         this.removeDropIndicators(n);
33552     },
33553     
33554     onNodeDrop : function(n, dd, e, data){
33555         var point = this.getDropPoint(e, n, dd);
33556         var targetNode = n.node;
33557         targetNode.ui.startDrop();
33558         if(!this.isValidDropPoint(n, point, dd, e, data)){
33559             targetNode.ui.endDrop();
33560             return false;
33561         }
33562         // first try to find the drop node
33563         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33564         var dropEvent = {
33565             tree : this.tree,
33566             target: targetNode,
33567             data: data,
33568             point: point,
33569             source: dd,
33570             rawEvent: e,
33571             dropNode: dropNode,
33572             cancel: !dropNode   
33573         };
33574         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33575         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33576             targetNode.ui.endDrop();
33577             return false;
33578         }
33579         // allow target changing
33580         targetNode = dropEvent.target;
33581         if(point == "append" && !targetNode.isExpanded()){
33582             targetNode.expand(false, null, function(){
33583                 this.completeDrop(dropEvent);
33584             }.createDelegate(this));
33585         }else{
33586             this.completeDrop(dropEvent);
33587         }
33588         return true;
33589     },
33590     
33591     completeDrop : function(de){
33592         var ns = de.dropNode, p = de.point, t = de.target;
33593         if(!(ns instanceof Array)){
33594             ns = [ns];
33595         }
33596         var n;
33597         for(var i = 0, len = ns.length; i < len; i++){
33598             n = ns[i];
33599             if(p == "above"){
33600                 t.parentNode.insertBefore(n, t);
33601             }else if(p == "below"){
33602                 t.parentNode.insertBefore(n, t.nextSibling);
33603             }else{
33604                 t.appendChild(n);
33605             }
33606         }
33607         n.ui.focus();
33608         if(this.tree.hlDrop){
33609             n.ui.highlight();
33610         }
33611         t.ui.endDrop();
33612         this.tree.fireEvent("nodedrop", de);
33613     },
33614     
33615     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33616         if(this.tree.hlDrop){
33617             dropNode.ui.focus();
33618             dropNode.ui.highlight();
33619         }
33620         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33621     },
33622     
33623     getTree : function(){
33624         return this.tree;
33625     },
33626     
33627     removeDropIndicators : function(n){
33628         if(n && n.ddel){
33629             var el = n.ddel;
33630             Roo.fly(el).removeClass([
33631                     "x-tree-drag-insert-above",
33632                     "x-tree-drag-insert-below",
33633                     "x-tree-drag-append"]);
33634             this.lastInsertClass = "_noclass";
33635         }
33636     },
33637     
33638     beforeDragDrop : function(target, e, id){
33639         this.cancelExpand();
33640         return true;
33641     },
33642     
33643     afterRepair : function(data){
33644         if(data && Roo.enableFx){
33645             data.node.ui.highlight();
33646         }
33647         this.hideProxy();
33648     }    
33649 });
33650
33651 }
33652 /*
33653  * Based on:
33654  * Ext JS Library 1.1.1
33655  * Copyright(c) 2006-2007, Ext JS, LLC.
33656  *
33657  * Originally Released Under LGPL - original licence link has changed is not relivant.
33658  *
33659  * Fork - LGPL
33660  * <script type="text/javascript">
33661  */
33662  
33663
33664 if(Roo.dd.DragZone){
33665 Roo.tree.TreeDragZone = function(tree, config){
33666     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33667     this.tree = tree;
33668 };
33669
33670 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33671     ddGroup : "TreeDD",
33672     
33673     onBeforeDrag : function(data, e){
33674         var n = data.node;
33675         return n && n.draggable && !n.disabled;
33676     },
33677     
33678     onInitDrag : function(e){
33679         var data = this.dragData;
33680         this.tree.getSelectionModel().select(data.node);
33681         this.proxy.update("");
33682         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33683         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33684     },
33685     
33686     getRepairXY : function(e, data){
33687         return data.node.ui.getDDRepairXY();
33688     },
33689     
33690     onEndDrag : function(data, e){
33691         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33692     },
33693     
33694     onValidDrop : function(dd, e, id){
33695         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33696         this.hideProxy();
33697     },
33698     
33699     beforeInvalidDrop : function(e, id){
33700         // this scrolls the original position back into view
33701         var sm = this.tree.getSelectionModel();
33702         sm.clearSelections();
33703         sm.select(this.dragData.node);
33704     }
33705 });
33706 }/*
33707  * Based on:
33708  * Ext JS Library 1.1.1
33709  * Copyright(c) 2006-2007, Ext JS, LLC.
33710  *
33711  * Originally Released Under LGPL - original licence link has changed is not relivant.
33712  *
33713  * Fork - LGPL
33714  * <script type="text/javascript">
33715  */
33716 /**
33717  * @class Roo.tree.TreeEditor
33718  * @extends Roo.Editor
33719  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33720  * as the editor field.
33721  * @constructor
33722  * @param {Object} config (used to be the tree panel.)
33723  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33724  * 
33725  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33726  * @cfg {Roo.form.TextField|Object} field The field configuration
33727  *
33728  * 
33729  */
33730 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33731     var tree = config;
33732     var field;
33733     if (oldconfig) { // old style..
33734         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33735     } else {
33736         // new style..
33737         tree = config.tree;
33738         config.field = config.field  || {};
33739         config.field.xtype = 'TextField';
33740         field = Roo.factory(config.field, Roo.form);
33741     }
33742     config = config || {};
33743     
33744     
33745     this.addEvents({
33746         /**
33747          * @event beforenodeedit
33748          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33749          * false from the handler of this event.
33750          * @param {Editor} this
33751          * @param {Roo.tree.Node} node 
33752          */
33753         "beforenodeedit" : true
33754     });
33755     
33756     //Roo.log(config);
33757     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33758
33759     this.tree = tree;
33760
33761     tree.on('beforeclick', this.beforeNodeClick, this);
33762     tree.getTreeEl().on('mousedown', this.hide, this);
33763     this.on('complete', this.updateNode, this);
33764     this.on('beforestartedit', this.fitToTree, this);
33765     this.on('startedit', this.bindScroll, this, {delay:10});
33766     this.on('specialkey', this.onSpecialKey, this);
33767 };
33768
33769 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33770     /**
33771      * @cfg {String} alignment
33772      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33773      */
33774     alignment: "l-l",
33775     // inherit
33776     autoSize: false,
33777     /**
33778      * @cfg {Boolean} hideEl
33779      * True to hide the bound element while the editor is displayed (defaults to false)
33780      */
33781     hideEl : false,
33782     /**
33783      * @cfg {String} cls
33784      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33785      */
33786     cls: "x-small-editor x-tree-editor",
33787     /**
33788      * @cfg {Boolean} shim
33789      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33790      */
33791     shim:false,
33792     // inherit
33793     shadow:"frame",
33794     /**
33795      * @cfg {Number} maxWidth
33796      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33797      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33798      * scroll and client offsets into account prior to each edit.
33799      */
33800     maxWidth: 250,
33801
33802     editDelay : 350,
33803
33804     // private
33805     fitToTree : function(ed, el){
33806         var td = this.tree.getTreeEl().dom, nd = el.dom;
33807         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33808             td.scrollLeft = nd.offsetLeft;
33809         }
33810         var w = Math.min(
33811                 this.maxWidth,
33812                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33813         this.setSize(w, '');
33814         
33815         return this.fireEvent('beforenodeedit', this, this.editNode);
33816         
33817     },
33818
33819     // private
33820     triggerEdit : function(node){
33821         this.completeEdit();
33822         this.editNode = node;
33823         this.startEdit(node.ui.textNode, node.text);
33824     },
33825
33826     // private
33827     bindScroll : function(){
33828         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33829     },
33830
33831     // private
33832     beforeNodeClick : function(node, e){
33833         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33834         this.lastClick = new Date();
33835         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33836             e.stopEvent();
33837             this.triggerEdit(node);
33838             return false;
33839         }
33840         return true;
33841     },
33842
33843     // private
33844     updateNode : function(ed, value){
33845         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33846         this.editNode.setText(value);
33847     },
33848
33849     // private
33850     onHide : function(){
33851         Roo.tree.TreeEditor.superclass.onHide.call(this);
33852         if(this.editNode){
33853             this.editNode.ui.focus();
33854         }
33855     },
33856
33857     // private
33858     onSpecialKey : function(field, e){
33859         var k = e.getKey();
33860         if(k == e.ESC){
33861             e.stopEvent();
33862             this.cancelEdit();
33863         }else if(k == e.ENTER && !e.hasModifier()){
33864             e.stopEvent();
33865             this.completeEdit();
33866         }
33867     }
33868 });//<Script type="text/javascript">
33869 /*
33870  * Based on:
33871  * Ext JS Library 1.1.1
33872  * Copyright(c) 2006-2007, Ext JS, LLC.
33873  *
33874  * Originally Released Under LGPL - original licence link has changed is not relivant.
33875  *
33876  * Fork - LGPL
33877  * <script type="text/javascript">
33878  */
33879  
33880 /**
33881  * Not documented??? - probably should be...
33882  */
33883
33884 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33885     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33886     
33887     renderElements : function(n, a, targetNode, bulkRender){
33888         //consel.log("renderElements?");
33889         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33890
33891         var t = n.getOwnerTree();
33892         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33893         
33894         var cols = t.columns;
33895         var bw = t.borderWidth;
33896         var c = cols[0];
33897         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33898          var cb = typeof a.checked == "boolean";
33899         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33900         var colcls = 'x-t-' + tid + '-c0';
33901         var buf = [
33902             '<li class="x-tree-node">',
33903             
33904                 
33905                 '<div class="x-tree-node-el ', a.cls,'">',
33906                     // extran...
33907                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33908                 
33909                 
33910                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33911                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33912                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33913                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33914                            (a.iconCls ? ' '+a.iconCls : ''),
33915                            '" unselectable="on" />',
33916                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33917                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33918                              
33919                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33920                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33921                             '<span unselectable="on" qtip="' + tx + '">',
33922                              tx,
33923                              '</span></a>' ,
33924                     '</div>',
33925                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33926                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33927                  ];
33928         for(var i = 1, len = cols.length; i < len; i++){
33929             c = cols[i];
33930             colcls = 'x-t-' + tid + '-c' +i;
33931             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33932             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33933                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33934                       "</div>");
33935          }
33936          
33937          buf.push(
33938             '</a>',
33939             '<div class="x-clear"></div></div>',
33940             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33941             "</li>");
33942         
33943         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33944             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33945                                 n.nextSibling.ui.getEl(), buf.join(""));
33946         }else{
33947             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33948         }
33949         var el = this.wrap.firstChild;
33950         this.elRow = el;
33951         this.elNode = el.firstChild;
33952         this.ranchor = el.childNodes[1];
33953         this.ctNode = this.wrap.childNodes[1];
33954         var cs = el.firstChild.childNodes;
33955         this.indentNode = cs[0];
33956         this.ecNode = cs[1];
33957         this.iconNode = cs[2];
33958         var index = 3;
33959         if(cb){
33960             this.checkbox = cs[3];
33961             index++;
33962         }
33963         this.anchor = cs[index];
33964         
33965         this.textNode = cs[index].firstChild;
33966         
33967         //el.on("click", this.onClick, this);
33968         //el.on("dblclick", this.onDblClick, this);
33969         
33970         
33971        // console.log(this);
33972     },
33973     initEvents : function(){
33974         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33975         
33976             
33977         var a = this.ranchor;
33978
33979         var el = Roo.get(a);
33980
33981         if(Roo.isOpera){ // opera render bug ignores the CSS
33982             el.setStyle("text-decoration", "none");
33983         }
33984
33985         el.on("click", this.onClick, this);
33986         el.on("dblclick", this.onDblClick, this);
33987         el.on("contextmenu", this.onContextMenu, this);
33988         
33989     },
33990     
33991     /*onSelectedChange : function(state){
33992         if(state){
33993             this.focus();
33994             this.addClass("x-tree-selected");
33995         }else{
33996             //this.blur();
33997             this.removeClass("x-tree-selected");
33998         }
33999     },*/
34000     addClass : function(cls){
34001         if(this.elRow){
34002             Roo.fly(this.elRow).addClass(cls);
34003         }
34004         
34005     },
34006     
34007     
34008     removeClass : function(cls){
34009         if(this.elRow){
34010             Roo.fly(this.elRow).removeClass(cls);
34011         }
34012     }
34013
34014     
34015     
34016 });//<Script type="text/javascript">
34017
34018 /*
34019  * Based on:
34020  * Ext JS Library 1.1.1
34021  * Copyright(c) 2006-2007, Ext JS, LLC.
34022  *
34023  * Originally Released Under LGPL - original licence link has changed is not relivant.
34024  *
34025  * Fork - LGPL
34026  * <script type="text/javascript">
34027  */
34028  
34029
34030 /**
34031  * @class Roo.tree.ColumnTree
34032  * @extends Roo.data.TreePanel
34033  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34034  * @cfg {int} borderWidth  compined right/left border allowance
34035  * @constructor
34036  * @param {String/HTMLElement/Element} el The container element
34037  * @param {Object} config
34038  */
34039 Roo.tree.ColumnTree =  function(el, config)
34040 {
34041    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34042    this.addEvents({
34043         /**
34044         * @event resize
34045         * Fire this event on a container when it resizes
34046         * @param {int} w Width
34047         * @param {int} h Height
34048         */
34049        "resize" : true
34050     });
34051     this.on('resize', this.onResize, this);
34052 };
34053
34054 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34055     //lines:false,
34056     
34057     
34058     borderWidth: Roo.isBorderBox ? 0 : 2, 
34059     headEls : false,
34060     
34061     render : function(){
34062         // add the header.....
34063        
34064         Roo.tree.ColumnTree.superclass.render.apply(this);
34065         
34066         this.el.addClass('x-column-tree');
34067         
34068         this.headers = this.el.createChild(
34069             {cls:'x-tree-headers'},this.innerCt.dom);
34070    
34071         var cols = this.columns, c;
34072         var totalWidth = 0;
34073         this.headEls = [];
34074         var  len = cols.length;
34075         for(var i = 0; i < len; i++){
34076              c = cols[i];
34077              totalWidth += c.width;
34078             this.headEls.push(this.headers.createChild({
34079                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34080                  cn: {
34081                      cls:'x-tree-hd-text',
34082                      html: c.header
34083                  },
34084                  style:'width:'+(c.width-this.borderWidth)+'px;'
34085              }));
34086         }
34087         this.headers.createChild({cls:'x-clear'});
34088         // prevent floats from wrapping when clipped
34089         this.headers.setWidth(totalWidth);
34090         //this.innerCt.setWidth(totalWidth);
34091         this.innerCt.setStyle({ overflow: 'auto' });
34092         this.onResize(this.width, this.height);
34093              
34094         
34095     },
34096     onResize : function(w,h)
34097     {
34098         this.height = h;
34099         this.width = w;
34100         // resize cols..
34101         this.innerCt.setWidth(this.width);
34102         this.innerCt.setHeight(this.height-20);
34103         
34104         // headers...
34105         var cols = this.columns, c;
34106         var totalWidth = 0;
34107         var expEl = false;
34108         var len = cols.length;
34109         for(var i = 0; i < len; i++){
34110             c = cols[i];
34111             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34112                 // it's the expander..
34113                 expEl  = this.headEls[i];
34114                 continue;
34115             }
34116             totalWidth += c.width;
34117             
34118         }
34119         if (expEl) {
34120             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34121         }
34122         this.headers.setWidth(w-20);
34123
34124         
34125         
34126         
34127     }
34128 });
34129 /*
34130  * Based on:
34131  * Ext JS Library 1.1.1
34132  * Copyright(c) 2006-2007, Ext JS, LLC.
34133  *
34134  * Originally Released Under LGPL - original licence link has changed is not relivant.
34135  *
34136  * Fork - LGPL
34137  * <script type="text/javascript">
34138  */
34139  
34140 /**
34141  * @class Roo.menu.Menu
34142  * @extends Roo.util.Observable
34143  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34144  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34145  * @constructor
34146  * Creates a new Menu
34147  * @param {Object} config Configuration options
34148  */
34149 Roo.menu.Menu = function(config){
34150     Roo.apply(this, config);
34151     this.id = this.id || Roo.id();
34152     this.addEvents({
34153         /**
34154          * @event beforeshow
34155          * Fires before this menu is displayed
34156          * @param {Roo.menu.Menu} this
34157          */
34158         beforeshow : true,
34159         /**
34160          * @event beforehide
34161          * Fires before this menu is hidden
34162          * @param {Roo.menu.Menu} this
34163          */
34164         beforehide : true,
34165         /**
34166          * @event show
34167          * Fires after this menu is displayed
34168          * @param {Roo.menu.Menu} this
34169          */
34170         show : true,
34171         /**
34172          * @event hide
34173          * Fires after this menu is hidden
34174          * @param {Roo.menu.Menu} this
34175          */
34176         hide : true,
34177         /**
34178          * @event click
34179          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34180          * @param {Roo.menu.Menu} this
34181          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34182          * @param {Roo.EventObject} e
34183          */
34184         click : true,
34185         /**
34186          * @event mouseover
34187          * Fires when the mouse is hovering over this menu
34188          * @param {Roo.menu.Menu} this
34189          * @param {Roo.EventObject} e
34190          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34191          */
34192         mouseover : true,
34193         /**
34194          * @event mouseout
34195          * Fires when the mouse exits this menu
34196          * @param {Roo.menu.Menu} this
34197          * @param {Roo.EventObject} e
34198          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34199          */
34200         mouseout : true,
34201         /**
34202          * @event itemclick
34203          * Fires when a menu item contained in this menu is clicked
34204          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34205          * @param {Roo.EventObject} e
34206          */
34207         itemclick: true
34208     });
34209     if (this.registerMenu) {
34210         Roo.menu.MenuMgr.register(this);
34211     }
34212     
34213     var mis = this.items;
34214     this.items = new Roo.util.MixedCollection();
34215     if(mis){
34216         this.add.apply(this, mis);
34217     }
34218 };
34219
34220 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34221     /**
34222      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34223      */
34224     minWidth : 120,
34225     /**
34226      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34227      * for bottom-right shadow (defaults to "sides")
34228      */
34229     shadow : "sides",
34230     /**
34231      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34232      * this menu (defaults to "tl-tr?")
34233      */
34234     subMenuAlign : "tl-tr?",
34235     /**
34236      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34237      * relative to its element of origin (defaults to "tl-bl?")
34238      */
34239     defaultAlign : "tl-bl?",
34240     /**
34241      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34242      */
34243     allowOtherMenus : false,
34244     /**
34245      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34246      */
34247     registerMenu : true,
34248
34249     hidden:true,
34250
34251     // private
34252     render : function(){
34253         if(this.el){
34254             return;
34255         }
34256         var el = this.el = new Roo.Layer({
34257             cls: "x-menu",
34258             shadow:this.shadow,
34259             constrain: false,
34260             parentEl: this.parentEl || document.body,
34261             zindex:15000
34262         });
34263
34264         this.keyNav = new Roo.menu.MenuNav(this);
34265
34266         if(this.plain){
34267             el.addClass("x-menu-plain");
34268         }
34269         if(this.cls){
34270             el.addClass(this.cls);
34271         }
34272         // generic focus element
34273         this.focusEl = el.createChild({
34274             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34275         });
34276         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34277         ul.on("click", this.onClick, this);
34278         ul.on("mouseover", this.onMouseOver, this);
34279         ul.on("mouseout", this.onMouseOut, this);
34280         this.items.each(function(item){
34281             var li = document.createElement("li");
34282             li.className = "x-menu-list-item";
34283             ul.dom.appendChild(li);
34284             item.render(li, this);
34285         }, this);
34286         this.ul = ul;
34287         this.autoWidth();
34288     },
34289
34290     // private
34291     autoWidth : function(){
34292         var el = this.el, ul = this.ul;
34293         if(!el){
34294             return;
34295         }
34296         var w = this.width;
34297         if(w){
34298             el.setWidth(w);
34299         }else if(Roo.isIE){
34300             el.setWidth(this.minWidth);
34301             var t = el.dom.offsetWidth; // force recalc
34302             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34303         }
34304     },
34305
34306     // private
34307     delayAutoWidth : function(){
34308         if(this.rendered){
34309             if(!this.awTask){
34310                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34311             }
34312             this.awTask.delay(20);
34313         }
34314     },
34315
34316     // private
34317     findTargetItem : function(e){
34318         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34319         if(t && t.menuItemId){
34320             return this.items.get(t.menuItemId);
34321         }
34322     },
34323
34324     // private
34325     onClick : function(e){
34326         var t;
34327         if(t = this.findTargetItem(e)){
34328             t.onClick(e);
34329             this.fireEvent("click", this, t, e);
34330         }
34331     },
34332
34333     // private
34334     setActiveItem : function(item, autoExpand){
34335         if(item != this.activeItem){
34336             if(this.activeItem){
34337                 this.activeItem.deactivate();
34338             }
34339             this.activeItem = item;
34340             item.activate(autoExpand);
34341         }else if(autoExpand){
34342             item.expandMenu();
34343         }
34344     },
34345
34346     // private
34347     tryActivate : function(start, step){
34348         var items = this.items;
34349         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34350             var item = items.get(i);
34351             if(!item.disabled && item.canActivate){
34352                 this.setActiveItem(item, false);
34353                 return item;
34354             }
34355         }
34356         return false;
34357     },
34358
34359     // private
34360     onMouseOver : function(e){
34361         var t;
34362         if(t = this.findTargetItem(e)){
34363             if(t.canActivate && !t.disabled){
34364                 this.setActiveItem(t, true);
34365             }
34366         }
34367         this.fireEvent("mouseover", this, e, t);
34368     },
34369
34370     // private
34371     onMouseOut : function(e){
34372         var t;
34373         if(t = this.findTargetItem(e)){
34374             if(t == this.activeItem && t.shouldDeactivate(e)){
34375                 this.activeItem.deactivate();
34376                 delete this.activeItem;
34377             }
34378         }
34379         this.fireEvent("mouseout", this, e, t);
34380     },
34381
34382     /**
34383      * Read-only.  Returns true if the menu is currently displayed, else false.
34384      * @type Boolean
34385      */
34386     isVisible : function(){
34387         return this.el && !this.hidden;
34388     },
34389
34390     /**
34391      * Displays this menu relative to another element
34392      * @param {String/HTMLElement/Roo.Element} element The element to align to
34393      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34394      * the element (defaults to this.defaultAlign)
34395      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34396      */
34397     show : function(el, pos, parentMenu){
34398         this.parentMenu = parentMenu;
34399         if(!this.el){
34400             this.render();
34401         }
34402         this.fireEvent("beforeshow", this);
34403         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34404     },
34405
34406     /**
34407      * Displays this menu at a specific xy position
34408      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34409      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34410      */
34411     showAt : function(xy, parentMenu, /* private: */_e){
34412         this.parentMenu = parentMenu;
34413         if(!this.el){
34414             this.render();
34415         }
34416         if(_e !== false){
34417             this.fireEvent("beforeshow", this);
34418             xy = this.el.adjustForConstraints(xy);
34419         }
34420         this.el.setXY(xy);
34421         this.el.show();
34422         this.hidden = false;
34423         this.focus();
34424         this.fireEvent("show", this);
34425     },
34426
34427     focus : function(){
34428         if(!this.hidden){
34429             this.doFocus.defer(50, this);
34430         }
34431     },
34432
34433     doFocus : function(){
34434         if(!this.hidden){
34435             this.focusEl.focus();
34436         }
34437     },
34438
34439     /**
34440      * Hides this menu and optionally all parent menus
34441      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34442      */
34443     hide : function(deep){
34444         if(this.el && this.isVisible()){
34445             this.fireEvent("beforehide", this);
34446             if(this.activeItem){
34447                 this.activeItem.deactivate();
34448                 this.activeItem = null;
34449             }
34450             this.el.hide();
34451             this.hidden = true;
34452             this.fireEvent("hide", this);
34453         }
34454         if(deep === true && this.parentMenu){
34455             this.parentMenu.hide(true);
34456         }
34457     },
34458
34459     /**
34460      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34461      * Any of the following are valid:
34462      * <ul>
34463      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34464      * <li>An HTMLElement object which will be converted to a menu item</li>
34465      * <li>A menu item config object that will be created as a new menu item</li>
34466      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34467      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34468      * </ul>
34469      * Usage:
34470      * <pre><code>
34471 // Create the menu
34472 var menu = new Roo.menu.Menu();
34473
34474 // Create a menu item to add by reference
34475 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34476
34477 // Add a bunch of items at once using different methods.
34478 // Only the last item added will be returned.
34479 var item = menu.add(
34480     menuItem,                // add existing item by ref
34481     'Dynamic Item',          // new TextItem
34482     '-',                     // new separator
34483     { text: 'Config Item' }  // new item by config
34484 );
34485 </code></pre>
34486      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34487      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34488      */
34489     add : function(){
34490         var a = arguments, l = a.length, item;
34491         for(var i = 0; i < l; i++){
34492             var el = a[i];
34493             if ((typeof(el) == "object") && el.xtype && el.xns) {
34494                 el = Roo.factory(el, Roo.menu);
34495             }
34496             
34497             if(el.render){ // some kind of Item
34498                 item = this.addItem(el);
34499             }else if(typeof el == "string"){ // string
34500                 if(el == "separator" || el == "-"){
34501                     item = this.addSeparator();
34502                 }else{
34503                     item = this.addText(el);
34504                 }
34505             }else if(el.tagName || el.el){ // element
34506                 item = this.addElement(el);
34507             }else if(typeof el == "object"){ // must be menu item config?
34508                 item = this.addMenuItem(el);
34509             }
34510         }
34511         return item;
34512     },
34513
34514     /**
34515      * Returns this menu's underlying {@link Roo.Element} object
34516      * @return {Roo.Element} The element
34517      */
34518     getEl : function(){
34519         if(!this.el){
34520             this.render();
34521         }
34522         return this.el;
34523     },
34524
34525     /**
34526      * Adds a separator bar to the menu
34527      * @return {Roo.menu.Item} The menu item that was added
34528      */
34529     addSeparator : function(){
34530         return this.addItem(new Roo.menu.Separator());
34531     },
34532
34533     /**
34534      * Adds an {@link Roo.Element} object to the menu
34535      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34536      * @return {Roo.menu.Item} The menu item that was added
34537      */
34538     addElement : function(el){
34539         return this.addItem(new Roo.menu.BaseItem(el));
34540     },
34541
34542     /**
34543      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34544      * @param {Roo.menu.Item} item The menu item to add
34545      * @return {Roo.menu.Item} The menu item that was added
34546      */
34547     addItem : function(item){
34548         this.items.add(item);
34549         if(this.ul){
34550             var li = document.createElement("li");
34551             li.className = "x-menu-list-item";
34552             this.ul.dom.appendChild(li);
34553             item.render(li, this);
34554             this.delayAutoWidth();
34555         }
34556         return item;
34557     },
34558
34559     /**
34560      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34561      * @param {Object} config A MenuItem config object
34562      * @return {Roo.menu.Item} The menu item that was added
34563      */
34564     addMenuItem : function(config){
34565         if(!(config instanceof Roo.menu.Item)){
34566             if(typeof config.checked == "boolean"){ // must be check menu item config?
34567                 config = new Roo.menu.CheckItem(config);
34568             }else{
34569                 config = new Roo.menu.Item(config);
34570             }
34571         }
34572         return this.addItem(config);
34573     },
34574
34575     /**
34576      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34577      * @param {String} text The text to display in the menu item
34578      * @return {Roo.menu.Item} The menu item that was added
34579      */
34580     addText : function(text){
34581         return this.addItem(new Roo.menu.TextItem({ text : text }));
34582     },
34583
34584     /**
34585      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34586      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34587      * @param {Roo.menu.Item} item The menu item to add
34588      * @return {Roo.menu.Item} The menu item that was added
34589      */
34590     insert : function(index, item){
34591         this.items.insert(index, item);
34592         if(this.ul){
34593             var li = document.createElement("li");
34594             li.className = "x-menu-list-item";
34595             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34596             item.render(li, this);
34597             this.delayAutoWidth();
34598         }
34599         return item;
34600     },
34601
34602     /**
34603      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34604      * @param {Roo.menu.Item} item The menu item to remove
34605      */
34606     remove : function(item){
34607         this.items.removeKey(item.id);
34608         item.destroy();
34609     },
34610
34611     /**
34612      * Removes and destroys all items in the menu
34613      */
34614     removeAll : function(){
34615         var f;
34616         while(f = this.items.first()){
34617             this.remove(f);
34618         }
34619     }
34620 });
34621
34622 // MenuNav is a private utility class used internally by the Menu
34623 Roo.menu.MenuNav = function(menu){
34624     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34625     this.scope = this.menu = menu;
34626 };
34627
34628 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34629     doRelay : function(e, h){
34630         var k = e.getKey();
34631         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34632             this.menu.tryActivate(0, 1);
34633             return false;
34634         }
34635         return h.call(this.scope || this, e, this.menu);
34636     },
34637
34638     up : function(e, m){
34639         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34640             m.tryActivate(m.items.length-1, -1);
34641         }
34642     },
34643
34644     down : function(e, m){
34645         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34646             m.tryActivate(0, 1);
34647         }
34648     },
34649
34650     right : function(e, m){
34651         if(m.activeItem){
34652             m.activeItem.expandMenu(true);
34653         }
34654     },
34655
34656     left : function(e, m){
34657         m.hide();
34658         if(m.parentMenu && m.parentMenu.activeItem){
34659             m.parentMenu.activeItem.activate();
34660         }
34661     },
34662
34663     enter : function(e, m){
34664         if(m.activeItem){
34665             e.stopPropagation();
34666             m.activeItem.onClick(e);
34667             m.fireEvent("click", this, m.activeItem);
34668             return true;
34669         }
34670     }
34671 });/*
34672  * Based on:
34673  * Ext JS Library 1.1.1
34674  * Copyright(c) 2006-2007, Ext JS, LLC.
34675  *
34676  * Originally Released Under LGPL - original licence link has changed is not relivant.
34677  *
34678  * Fork - LGPL
34679  * <script type="text/javascript">
34680  */
34681  
34682 /**
34683  * @class Roo.menu.MenuMgr
34684  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34685  * @singleton
34686  */
34687 Roo.menu.MenuMgr = function(){
34688    var menus, active, groups = {}, attached = false, lastShow = new Date();
34689
34690    // private - called when first menu is created
34691    function init(){
34692        menus = {};
34693        active = new Roo.util.MixedCollection();
34694        Roo.get(document).addKeyListener(27, function(){
34695            if(active.length > 0){
34696                hideAll();
34697            }
34698        });
34699    }
34700
34701    // private
34702    function hideAll(){
34703        if(active && active.length > 0){
34704            var c = active.clone();
34705            c.each(function(m){
34706                m.hide();
34707            });
34708        }
34709    }
34710
34711    // private
34712    function onHide(m){
34713        active.remove(m);
34714        if(active.length < 1){
34715            Roo.get(document).un("mousedown", onMouseDown);
34716            attached = false;
34717        }
34718    }
34719
34720    // private
34721    function onShow(m){
34722        var last = active.last();
34723        lastShow = new Date();
34724        active.add(m);
34725        if(!attached){
34726            Roo.get(document).on("mousedown", onMouseDown);
34727            attached = true;
34728        }
34729        if(m.parentMenu){
34730           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34731           m.parentMenu.activeChild = m;
34732        }else if(last && last.isVisible()){
34733           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34734        }
34735    }
34736
34737    // private
34738    function onBeforeHide(m){
34739        if(m.activeChild){
34740            m.activeChild.hide();
34741        }
34742        if(m.autoHideTimer){
34743            clearTimeout(m.autoHideTimer);
34744            delete m.autoHideTimer;
34745        }
34746    }
34747
34748    // private
34749    function onBeforeShow(m){
34750        var pm = m.parentMenu;
34751        if(!pm && !m.allowOtherMenus){
34752            hideAll();
34753        }else if(pm && pm.activeChild && active != m){
34754            pm.activeChild.hide();
34755        }
34756    }
34757
34758    // private
34759    function onMouseDown(e){
34760        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34761            hideAll();
34762        }
34763    }
34764
34765    // private
34766    function onBeforeCheck(mi, state){
34767        if(state){
34768            var g = groups[mi.group];
34769            for(var i = 0, l = g.length; i < l; i++){
34770                if(g[i] != mi){
34771                    g[i].setChecked(false);
34772                }
34773            }
34774        }
34775    }
34776
34777    return {
34778
34779        /**
34780         * Hides all menus that are currently visible
34781         */
34782        hideAll : function(){
34783             hideAll();  
34784        },
34785
34786        // private
34787        register : function(menu){
34788            if(!menus){
34789                init();
34790            }
34791            menus[menu.id] = menu;
34792            menu.on("beforehide", onBeforeHide);
34793            menu.on("hide", onHide);
34794            menu.on("beforeshow", onBeforeShow);
34795            menu.on("show", onShow);
34796            var g = menu.group;
34797            if(g && menu.events["checkchange"]){
34798                if(!groups[g]){
34799                    groups[g] = [];
34800                }
34801                groups[g].push(menu);
34802                menu.on("checkchange", onCheck);
34803            }
34804        },
34805
34806         /**
34807          * Returns a {@link Roo.menu.Menu} object
34808          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34809          * be used to generate and return a new Menu instance.
34810          */
34811        get : function(menu){
34812            if(typeof menu == "string"){ // menu id
34813                return menus[menu];
34814            }else if(menu.events){  // menu instance
34815                return menu;
34816            }else if(typeof menu.length == 'number'){ // array of menu items?
34817                return new Roo.menu.Menu({items:menu});
34818            }else{ // otherwise, must be a config
34819                return new Roo.menu.Menu(menu);
34820            }
34821        },
34822
34823        // private
34824        unregister : function(menu){
34825            delete menus[menu.id];
34826            menu.un("beforehide", onBeforeHide);
34827            menu.un("hide", onHide);
34828            menu.un("beforeshow", onBeforeShow);
34829            menu.un("show", onShow);
34830            var g = menu.group;
34831            if(g && menu.events["checkchange"]){
34832                groups[g].remove(menu);
34833                menu.un("checkchange", onCheck);
34834            }
34835        },
34836
34837        // private
34838        registerCheckable : function(menuItem){
34839            var g = menuItem.group;
34840            if(g){
34841                if(!groups[g]){
34842                    groups[g] = [];
34843                }
34844                groups[g].push(menuItem);
34845                menuItem.on("beforecheckchange", onBeforeCheck);
34846            }
34847        },
34848
34849        // private
34850        unregisterCheckable : function(menuItem){
34851            var g = menuItem.group;
34852            if(g){
34853                groups[g].remove(menuItem);
34854                menuItem.un("beforecheckchange", onBeforeCheck);
34855            }
34856        }
34857    };
34858 }();/*
34859  * Based on:
34860  * Ext JS Library 1.1.1
34861  * Copyright(c) 2006-2007, Ext JS, LLC.
34862  *
34863  * Originally Released Under LGPL - original licence link has changed is not relivant.
34864  *
34865  * Fork - LGPL
34866  * <script type="text/javascript">
34867  */
34868  
34869
34870 /**
34871  * @class Roo.menu.BaseItem
34872  * @extends Roo.Component
34873  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34874  * management and base configuration options shared by all menu components.
34875  * @constructor
34876  * Creates a new BaseItem
34877  * @param {Object} config Configuration options
34878  */
34879 Roo.menu.BaseItem = function(config){
34880     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34881
34882     this.addEvents({
34883         /**
34884          * @event click
34885          * Fires when this item is clicked
34886          * @param {Roo.menu.BaseItem} this
34887          * @param {Roo.EventObject} e
34888          */
34889         click: true,
34890         /**
34891          * @event activate
34892          * Fires when this item is activated
34893          * @param {Roo.menu.BaseItem} this
34894          */
34895         activate : true,
34896         /**
34897          * @event deactivate
34898          * Fires when this item is deactivated
34899          * @param {Roo.menu.BaseItem} this
34900          */
34901         deactivate : true
34902     });
34903
34904     if(this.handler){
34905         this.on("click", this.handler, this.scope, true);
34906     }
34907 };
34908
34909 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34910     /**
34911      * @cfg {Function} handler
34912      * A function that will handle the click event of this menu item (defaults to undefined)
34913      */
34914     /**
34915      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34916      */
34917     canActivate : false,
34918     /**
34919      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34920      */
34921     activeClass : "x-menu-item-active",
34922     /**
34923      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34924      */
34925     hideOnClick : true,
34926     /**
34927      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34928      */
34929     hideDelay : 100,
34930
34931     // private
34932     ctype: "Roo.menu.BaseItem",
34933
34934     // private
34935     actionMode : "container",
34936
34937     // private
34938     render : function(container, parentMenu){
34939         this.parentMenu = parentMenu;
34940         Roo.menu.BaseItem.superclass.render.call(this, container);
34941         this.container.menuItemId = this.id;
34942     },
34943
34944     // private
34945     onRender : function(container, position){
34946         this.el = Roo.get(this.el);
34947         container.dom.appendChild(this.el.dom);
34948     },
34949
34950     // private
34951     onClick : function(e){
34952         if(!this.disabled && this.fireEvent("click", this, e) !== false
34953                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34954             this.handleClick(e);
34955         }else{
34956             e.stopEvent();
34957         }
34958     },
34959
34960     // private
34961     activate : function(){
34962         if(this.disabled){
34963             return false;
34964         }
34965         var li = this.container;
34966         li.addClass(this.activeClass);
34967         this.region = li.getRegion().adjust(2, 2, -2, -2);
34968         this.fireEvent("activate", this);
34969         return true;
34970     },
34971
34972     // private
34973     deactivate : function(){
34974         this.container.removeClass(this.activeClass);
34975         this.fireEvent("deactivate", this);
34976     },
34977
34978     // private
34979     shouldDeactivate : function(e){
34980         return !this.region || !this.region.contains(e.getPoint());
34981     },
34982
34983     // private
34984     handleClick : function(e){
34985         if(this.hideOnClick){
34986             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34987         }
34988     },
34989
34990     // private
34991     expandMenu : function(autoActivate){
34992         // do nothing
34993     },
34994
34995     // private
34996     hideMenu : function(){
34997         // do nothing
34998     }
34999 });/*
35000  * Based on:
35001  * Ext JS Library 1.1.1
35002  * Copyright(c) 2006-2007, Ext JS, LLC.
35003  *
35004  * Originally Released Under LGPL - original licence link has changed is not relivant.
35005  *
35006  * Fork - LGPL
35007  * <script type="text/javascript">
35008  */
35009  
35010 /**
35011  * @class Roo.menu.Adapter
35012  * @extends Roo.menu.BaseItem
35013  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
35014  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35015  * @constructor
35016  * Creates a new Adapter
35017  * @param {Object} config Configuration options
35018  */
35019 Roo.menu.Adapter = function(component, config){
35020     Roo.menu.Adapter.superclass.constructor.call(this, config);
35021     this.component = component;
35022 };
35023 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35024     // private
35025     canActivate : true,
35026
35027     // private
35028     onRender : function(container, position){
35029         this.component.render(container);
35030         this.el = this.component.getEl();
35031     },
35032
35033     // private
35034     activate : function(){
35035         if(this.disabled){
35036             return false;
35037         }
35038         this.component.focus();
35039         this.fireEvent("activate", this);
35040         return true;
35041     },
35042
35043     // private
35044     deactivate : function(){
35045         this.fireEvent("deactivate", this);
35046     },
35047
35048     // private
35049     disable : function(){
35050         this.component.disable();
35051         Roo.menu.Adapter.superclass.disable.call(this);
35052     },
35053
35054     // private
35055     enable : function(){
35056         this.component.enable();
35057         Roo.menu.Adapter.superclass.enable.call(this);
35058     }
35059 });/*
35060  * Based on:
35061  * Ext JS Library 1.1.1
35062  * Copyright(c) 2006-2007, Ext JS, LLC.
35063  *
35064  * Originally Released Under LGPL - original licence link has changed is not relivant.
35065  *
35066  * Fork - LGPL
35067  * <script type="text/javascript">
35068  */
35069
35070 /**
35071  * @class Roo.menu.TextItem
35072  * @extends Roo.menu.BaseItem
35073  * Adds a static text string to a menu, usually used as either a heading or group separator.
35074  * Note: old style constructor with text is still supported.
35075  * 
35076  * @constructor
35077  * Creates a new TextItem
35078  * @param {Object} cfg Configuration
35079  */
35080 Roo.menu.TextItem = function(cfg){
35081     if (typeof(cfg) == 'string') {
35082         this.text = cfg;
35083     } else {
35084         Roo.apply(this,cfg);
35085     }
35086     
35087     Roo.menu.TextItem.superclass.constructor.call(this);
35088 };
35089
35090 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35091     /**
35092      * @cfg {Boolean} text Text to show on item.
35093      */
35094     text : '',
35095     
35096     /**
35097      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35098      */
35099     hideOnClick : false,
35100     /**
35101      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35102      */
35103     itemCls : "x-menu-text",
35104
35105     // private
35106     onRender : function(){
35107         var s = document.createElement("span");
35108         s.className = this.itemCls;
35109         s.innerHTML = this.text;
35110         this.el = s;
35111         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35112     }
35113 });/*
35114  * Based on:
35115  * Ext JS Library 1.1.1
35116  * Copyright(c) 2006-2007, Ext JS, LLC.
35117  *
35118  * Originally Released Under LGPL - original licence link has changed is not relivant.
35119  *
35120  * Fork - LGPL
35121  * <script type="text/javascript">
35122  */
35123
35124 /**
35125  * @class Roo.menu.Separator
35126  * @extends Roo.menu.BaseItem
35127  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35128  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35129  * @constructor
35130  * @param {Object} config Configuration options
35131  */
35132 Roo.menu.Separator = function(config){
35133     Roo.menu.Separator.superclass.constructor.call(this, config);
35134 };
35135
35136 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35137     /**
35138      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35139      */
35140     itemCls : "x-menu-sep",
35141     /**
35142      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35143      */
35144     hideOnClick : false,
35145
35146     // private
35147     onRender : function(li){
35148         var s = document.createElement("span");
35149         s.className = this.itemCls;
35150         s.innerHTML = "&#160;";
35151         this.el = s;
35152         li.addClass("x-menu-sep-li");
35153         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35154     }
35155 });/*
35156  * Based on:
35157  * Ext JS Library 1.1.1
35158  * Copyright(c) 2006-2007, Ext JS, LLC.
35159  *
35160  * Originally Released Under LGPL - original licence link has changed is not relivant.
35161  *
35162  * Fork - LGPL
35163  * <script type="text/javascript">
35164  */
35165 /**
35166  * @class Roo.menu.Item
35167  * @extends Roo.menu.BaseItem
35168  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35169  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35170  * activation and click handling.
35171  * @constructor
35172  * Creates a new Item
35173  * @param {Object} config Configuration options
35174  */
35175 Roo.menu.Item = function(config){
35176     Roo.menu.Item.superclass.constructor.call(this, config);
35177     if(this.menu){
35178         this.menu = Roo.menu.MenuMgr.get(this.menu);
35179     }
35180 };
35181 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35182     
35183     /**
35184      * @cfg {String} text
35185      * The text to show on the menu item.
35186      */
35187     text: '',
35188      /**
35189      * @cfg {String} HTML to render in menu
35190      * The text to show on the menu item (HTML version).
35191      */
35192     html: '',
35193     /**
35194      * @cfg {String} icon
35195      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35196      */
35197     icon: undefined,
35198     /**
35199      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35200      */
35201     itemCls : "x-menu-item",
35202     /**
35203      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35204      */
35205     canActivate : true,
35206     /**
35207      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35208      */
35209     showDelay: 200,
35210     // doc'd in BaseItem
35211     hideDelay: 200,
35212
35213     // private
35214     ctype: "Roo.menu.Item",
35215     
35216     // private
35217     onRender : function(container, position){
35218         var el = document.createElement("a");
35219         el.hideFocus = true;
35220         el.unselectable = "on";
35221         el.href = this.href || "#";
35222         if(this.hrefTarget){
35223             el.target = this.hrefTarget;
35224         }
35225         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35226         
35227         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35228         
35229         el.innerHTML = String.format(
35230                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35231                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35232         this.el = el;
35233         Roo.menu.Item.superclass.onRender.call(this, container, position);
35234     },
35235
35236     /**
35237      * Sets the text to display in this menu item
35238      * @param {String} text The text to display
35239      * @param {Boolean} isHTML true to indicate text is pure html.
35240      */
35241     setText : function(text, isHTML){
35242         if (isHTML) {
35243             this.html = text;
35244         } else {
35245             this.text = text;
35246             this.html = '';
35247         }
35248         if(this.rendered){
35249             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35250      
35251             this.el.update(String.format(
35252                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35253                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35254             this.parentMenu.autoWidth();
35255         }
35256     },
35257
35258     // private
35259     handleClick : function(e){
35260         if(!this.href){ // if no link defined, stop the event automatically
35261             e.stopEvent();
35262         }
35263         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35264     },
35265
35266     // private
35267     activate : function(autoExpand){
35268         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35269             this.focus();
35270             if(autoExpand){
35271                 this.expandMenu();
35272             }
35273         }
35274         return true;
35275     },
35276
35277     // private
35278     shouldDeactivate : function(e){
35279         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35280             if(this.menu && this.menu.isVisible()){
35281                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35282             }
35283             return true;
35284         }
35285         return false;
35286     },
35287
35288     // private
35289     deactivate : function(){
35290         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35291         this.hideMenu();
35292     },
35293
35294     // private
35295     expandMenu : function(autoActivate){
35296         if(!this.disabled && this.menu){
35297             clearTimeout(this.hideTimer);
35298             delete this.hideTimer;
35299             if(!this.menu.isVisible() && !this.showTimer){
35300                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35301             }else if (this.menu.isVisible() && autoActivate){
35302                 this.menu.tryActivate(0, 1);
35303             }
35304         }
35305     },
35306
35307     // private
35308     deferExpand : function(autoActivate){
35309         delete this.showTimer;
35310         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35311         if(autoActivate){
35312             this.menu.tryActivate(0, 1);
35313         }
35314     },
35315
35316     // private
35317     hideMenu : function(){
35318         clearTimeout(this.showTimer);
35319         delete this.showTimer;
35320         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35321             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35322         }
35323     },
35324
35325     // private
35326     deferHide : function(){
35327         delete this.hideTimer;
35328         this.menu.hide();
35329     }
35330 });/*
35331  * Based on:
35332  * Ext JS Library 1.1.1
35333  * Copyright(c) 2006-2007, Ext JS, LLC.
35334  *
35335  * Originally Released Under LGPL - original licence link has changed is not relivant.
35336  *
35337  * Fork - LGPL
35338  * <script type="text/javascript">
35339  */
35340  
35341 /**
35342  * @class Roo.menu.CheckItem
35343  * @extends Roo.menu.Item
35344  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35345  * @constructor
35346  * Creates a new CheckItem
35347  * @param {Object} config Configuration options
35348  */
35349 Roo.menu.CheckItem = function(config){
35350     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35351     this.addEvents({
35352         /**
35353          * @event beforecheckchange
35354          * Fires before the checked value is set, providing an opportunity to cancel if needed
35355          * @param {Roo.menu.CheckItem} this
35356          * @param {Boolean} checked The new checked value that will be set
35357          */
35358         "beforecheckchange" : true,
35359         /**
35360          * @event checkchange
35361          * Fires after the checked value has been set
35362          * @param {Roo.menu.CheckItem} this
35363          * @param {Boolean} checked The checked value that was set
35364          */
35365         "checkchange" : true
35366     });
35367     if(this.checkHandler){
35368         this.on('checkchange', this.checkHandler, this.scope);
35369     }
35370 };
35371 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35372     /**
35373      * @cfg {String} group
35374      * All check items with the same group name will automatically be grouped into a single-select
35375      * radio button group (defaults to '')
35376      */
35377     /**
35378      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35379      */
35380     itemCls : "x-menu-item x-menu-check-item",
35381     /**
35382      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35383      */
35384     groupClass : "x-menu-group-item",
35385
35386     /**
35387      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35388      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35389      * initialized with checked = true will be rendered as checked.
35390      */
35391     checked: false,
35392
35393     // private
35394     ctype: "Roo.menu.CheckItem",
35395
35396     // private
35397     onRender : function(c){
35398         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35399         if(this.group){
35400             this.el.addClass(this.groupClass);
35401         }
35402         Roo.menu.MenuMgr.registerCheckable(this);
35403         if(this.checked){
35404             this.checked = false;
35405             this.setChecked(true, true);
35406         }
35407     },
35408
35409     // private
35410     destroy : function(){
35411         if(this.rendered){
35412             Roo.menu.MenuMgr.unregisterCheckable(this);
35413         }
35414         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35415     },
35416
35417     /**
35418      * Set the checked state of this item
35419      * @param {Boolean} checked The new checked value
35420      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35421      */
35422     setChecked : function(state, suppressEvent){
35423         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35424             if(this.container){
35425                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35426             }
35427             this.checked = state;
35428             if(suppressEvent !== true){
35429                 this.fireEvent("checkchange", this, state);
35430             }
35431         }
35432     },
35433
35434     // private
35435     handleClick : function(e){
35436        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35437            this.setChecked(!this.checked);
35438        }
35439        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35440     }
35441 });/*
35442  * Based on:
35443  * Ext JS Library 1.1.1
35444  * Copyright(c) 2006-2007, Ext JS, LLC.
35445  *
35446  * Originally Released Under LGPL - original licence link has changed is not relivant.
35447  *
35448  * Fork - LGPL
35449  * <script type="text/javascript">
35450  */
35451  
35452 /**
35453  * @class Roo.menu.DateItem
35454  * @extends Roo.menu.Adapter
35455  * A menu item that wraps the {@link Roo.DatPicker} component.
35456  * @constructor
35457  * Creates a new DateItem
35458  * @param {Object} config Configuration options
35459  */
35460 Roo.menu.DateItem = function(config){
35461     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35462     /** The Roo.DatePicker object @type Roo.DatePicker */
35463     this.picker = this.component;
35464     this.addEvents({select: true});
35465     
35466     this.picker.on("render", function(picker){
35467         picker.getEl().swallowEvent("click");
35468         picker.container.addClass("x-menu-date-item");
35469     });
35470
35471     this.picker.on("select", this.onSelect, this);
35472 };
35473
35474 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35475     // private
35476     onSelect : function(picker, date){
35477         this.fireEvent("select", this, date, picker);
35478         Roo.menu.DateItem.superclass.handleClick.call(this);
35479     }
35480 });/*
35481  * Based on:
35482  * Ext JS Library 1.1.1
35483  * Copyright(c) 2006-2007, Ext JS, LLC.
35484  *
35485  * Originally Released Under LGPL - original licence link has changed is not relivant.
35486  *
35487  * Fork - LGPL
35488  * <script type="text/javascript">
35489  */
35490  
35491 /**
35492  * @class Roo.menu.ColorItem
35493  * @extends Roo.menu.Adapter
35494  * A menu item that wraps the {@link Roo.ColorPalette} component.
35495  * @constructor
35496  * Creates a new ColorItem
35497  * @param {Object} config Configuration options
35498  */
35499 Roo.menu.ColorItem = function(config){
35500     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35501     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35502     this.palette = this.component;
35503     this.relayEvents(this.palette, ["select"]);
35504     if(this.selectHandler){
35505         this.on('select', this.selectHandler, this.scope);
35506     }
35507 };
35508 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35509  * Based on:
35510  * Ext JS Library 1.1.1
35511  * Copyright(c) 2006-2007, Ext JS, LLC.
35512  *
35513  * Originally Released Under LGPL - original licence link has changed is not relivant.
35514  *
35515  * Fork - LGPL
35516  * <script type="text/javascript">
35517  */
35518  
35519
35520 /**
35521  * @class Roo.menu.DateMenu
35522  * @extends Roo.menu.Menu
35523  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35524  * @constructor
35525  * Creates a new DateMenu
35526  * @param {Object} config Configuration options
35527  */
35528 Roo.menu.DateMenu = function(config){
35529     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35530     this.plain = true;
35531     var di = new Roo.menu.DateItem(config);
35532     this.add(di);
35533     /**
35534      * The {@link Roo.DatePicker} instance for this DateMenu
35535      * @type DatePicker
35536      */
35537     this.picker = di.picker;
35538     /**
35539      * @event select
35540      * @param {DatePicker} picker
35541      * @param {Date} date
35542      */
35543     this.relayEvents(di, ["select"]);
35544
35545     this.on('beforeshow', function(){
35546         if(this.picker){
35547             this.picker.hideMonthPicker(true);
35548         }
35549     }, this);
35550 };
35551 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35552     cls:'x-date-menu'
35553 });/*
35554  * Based on:
35555  * Ext JS Library 1.1.1
35556  * Copyright(c) 2006-2007, Ext JS, LLC.
35557  *
35558  * Originally Released Under LGPL - original licence link has changed is not relivant.
35559  *
35560  * Fork - LGPL
35561  * <script type="text/javascript">
35562  */
35563  
35564
35565 /**
35566  * @class Roo.menu.ColorMenu
35567  * @extends Roo.menu.Menu
35568  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35569  * @constructor
35570  * Creates a new ColorMenu
35571  * @param {Object} config Configuration options
35572  */
35573 Roo.menu.ColorMenu = function(config){
35574     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35575     this.plain = true;
35576     var ci = new Roo.menu.ColorItem(config);
35577     this.add(ci);
35578     /**
35579      * The {@link Roo.ColorPalette} instance for this ColorMenu
35580      * @type ColorPalette
35581      */
35582     this.palette = ci.palette;
35583     /**
35584      * @event select
35585      * @param {ColorPalette} palette
35586      * @param {String} color
35587      */
35588     this.relayEvents(ci, ["select"]);
35589 };
35590 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35591  * Based on:
35592  * Ext JS Library 1.1.1
35593  * Copyright(c) 2006-2007, Ext JS, LLC.
35594  *
35595  * Originally Released Under LGPL - original licence link has changed is not relivant.
35596  *
35597  * Fork - LGPL
35598  * <script type="text/javascript">
35599  */
35600  
35601 /**
35602  * @class Roo.form.Field
35603  * @extends Roo.BoxComponent
35604  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35605  * @constructor
35606  * Creates a new Field
35607  * @param {Object} config Configuration options
35608  */
35609 Roo.form.Field = function(config){
35610     Roo.form.Field.superclass.constructor.call(this, config);
35611 };
35612
35613 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35614     /**
35615      * @cfg {String} fieldLabel Label to use when rendering a form.
35616      */
35617        /**
35618      * @cfg {String} qtip Mouse over tip
35619      */
35620      
35621     /**
35622      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35623      */
35624     invalidClass : "x-form-invalid",
35625     /**
35626      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
35627      */
35628     invalidText : "The value in this field is invalid",
35629     /**
35630      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35631      */
35632     focusClass : "x-form-focus",
35633     /**
35634      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35635       automatic validation (defaults to "keyup").
35636      */
35637     validationEvent : "keyup",
35638     /**
35639      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35640      */
35641     validateOnBlur : true,
35642     /**
35643      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35644      */
35645     validationDelay : 250,
35646     /**
35647      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35648      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35649      */
35650     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35651     /**
35652      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35653      */
35654     fieldClass : "x-form-field",
35655     /**
35656      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35657      *<pre>
35658 Value         Description
35659 -----------   ----------------------------------------------------------------------
35660 qtip          Display a quick tip when the user hovers over the field
35661 title         Display a default browser title attribute popup
35662 under         Add a block div beneath the field containing the error text
35663 side          Add an error icon to the right of the field with a popup on hover
35664 [element id]  Add the error text directly to the innerHTML of the specified element
35665 </pre>
35666      */
35667     msgTarget : 'qtip',
35668     /**
35669      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35670      */
35671     msgFx : 'normal',
35672
35673     /**
35674      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
35675      */
35676     readOnly : false,
35677
35678     /**
35679      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35680      */
35681     disabled : false,
35682
35683     /**
35684      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35685      */
35686     inputType : undefined,
35687     
35688     /**
35689      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
35690          */
35691         tabIndex : undefined,
35692         
35693     // private
35694     isFormField : true,
35695
35696     // private
35697     hasFocus : false,
35698     /**
35699      * @property {Roo.Element} fieldEl
35700      * Element Containing the rendered Field (with label etc.)
35701      */
35702     /**
35703      * @cfg {Mixed} value A value to initialize this field with.
35704      */
35705     value : undefined,
35706
35707     /**
35708      * @cfg {String} name The field's HTML name attribute.
35709      */
35710     /**
35711      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35712      */
35713
35714         // private ??
35715         initComponent : function(){
35716         Roo.form.Field.superclass.initComponent.call(this);
35717         this.addEvents({
35718             /**
35719              * @event focus
35720              * Fires when this field receives input focus.
35721              * @param {Roo.form.Field} this
35722              */
35723             focus : true,
35724             /**
35725              * @event blur
35726              * Fires when this field loses input focus.
35727              * @param {Roo.form.Field} this
35728              */
35729             blur : true,
35730             /**
35731              * @event specialkey
35732              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35733              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35734              * @param {Roo.form.Field} this
35735              * @param {Roo.EventObject} e The event object
35736              */
35737             specialkey : true,
35738             /**
35739              * @event change
35740              * Fires just before the field blurs if the field value has changed.
35741              * @param {Roo.form.Field} this
35742              * @param {Mixed} newValue The new value
35743              * @param {Mixed} oldValue The original value
35744              */
35745             change : true,
35746             /**
35747              * @event invalid
35748              * Fires after the field has been marked as invalid.
35749              * @param {Roo.form.Field} this
35750              * @param {String} msg The validation message
35751              */
35752             invalid : true,
35753             /**
35754              * @event valid
35755              * Fires after the field has been validated with no errors.
35756              * @param {Roo.form.Field} this
35757              */
35758             valid : true,
35759              /**
35760              * @event keyup
35761              * Fires after the key up
35762              * @param {Roo.form.Field} this
35763              * @param {Roo.EventObject}  e The event Object
35764              */
35765             keyup : true
35766         });
35767     },
35768
35769     /**
35770      * Returns the name attribute of the field if available
35771      * @return {String} name The field name
35772      */
35773     getName: function(){
35774          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35775     },
35776
35777     // private
35778     onRender : function(ct, position){
35779         Roo.form.Field.superclass.onRender.call(this, ct, position);
35780         if(!this.el){
35781             var cfg = this.getAutoCreate();
35782             if(!cfg.name){
35783                 cfg.name = this.name || this.id;
35784             }
35785             if(this.inputType){
35786                 cfg.type = this.inputType;
35787             }
35788             this.el = ct.createChild(cfg, position);
35789         }
35790         var type = this.el.dom.type;
35791         if(type){
35792             if(type == 'password'){
35793                 type = 'text';
35794             }
35795             this.el.addClass('x-form-'+type);
35796         }
35797         if(this.readOnly){
35798             this.el.dom.readOnly = true;
35799         }
35800         if(this.tabIndex !== undefined){
35801             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35802         }
35803
35804         this.el.addClass([this.fieldClass, this.cls]);
35805         this.initValue();
35806     },
35807
35808     /**
35809      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35810      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35811      * @return {Roo.form.Field} this
35812      */
35813     applyTo : function(target){
35814         this.allowDomMove = false;
35815         this.el = Roo.get(target);
35816         this.render(this.el.dom.parentNode);
35817         return this;
35818     },
35819
35820     // private
35821     initValue : function(){
35822         if(this.value !== undefined){
35823             this.setValue(this.value);
35824         }else if(this.el.dom.value.length > 0){
35825             this.setValue(this.el.dom.value);
35826         }
35827     },
35828
35829     /**
35830      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35831      */
35832     isDirty : function() {
35833         if(this.disabled) {
35834             return false;
35835         }
35836         return String(this.getValue()) !== String(this.originalValue);
35837     },
35838
35839     // private
35840     afterRender : function(){
35841         Roo.form.Field.superclass.afterRender.call(this);
35842         this.initEvents();
35843     },
35844
35845     // private
35846     fireKey : function(e){
35847         //Roo.log('field ' + e.getKey());
35848         if(e.isNavKeyPress()){
35849             this.fireEvent("specialkey", this, e);
35850         }
35851     },
35852
35853     /**
35854      * Resets the current field value to the originally loaded value and clears any validation messages
35855      */
35856     reset : function(){
35857         this.setValue(this.originalValue);
35858         this.clearInvalid();
35859     },
35860
35861     // private
35862     initEvents : function(){
35863         // safari killled keypress - so keydown is now used..
35864         this.el.on("keydown" , this.fireKey,  this);
35865         this.el.on("focus", this.onFocus,  this);
35866         this.el.on("blur", this.onBlur,  this);
35867         this.el.relayEvent('keyup', this);
35868
35869         // reference to original value for reset
35870         this.originalValue = this.getValue();
35871     },
35872
35873     // private
35874     onFocus : function(){
35875         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35876             this.el.addClass(this.focusClass);
35877         }
35878         if(!this.hasFocus){
35879             this.hasFocus = true;
35880             this.startValue = this.getValue();
35881             this.fireEvent("focus", this);
35882         }
35883     },
35884
35885     beforeBlur : Roo.emptyFn,
35886
35887     // private
35888     onBlur : function(){
35889         this.beforeBlur();
35890         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35891             this.el.removeClass(this.focusClass);
35892         }
35893         this.hasFocus = false;
35894         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35895             this.validate();
35896         }
35897         var v = this.getValue();
35898         if(String(v) !== String(this.startValue)){
35899             this.fireEvent('change', this, v, this.startValue);
35900         }
35901         this.fireEvent("blur", this);
35902     },
35903
35904     /**
35905      * Returns whether or not the field value is currently valid
35906      * @param {Boolean} preventMark True to disable marking the field invalid
35907      * @return {Boolean} True if the value is valid, else false
35908      */
35909     isValid : function(preventMark){
35910         if(this.disabled){
35911             return true;
35912         }
35913         var restore = this.preventMark;
35914         this.preventMark = preventMark === true;
35915         var v = this.validateValue(this.processValue(this.getRawValue()));
35916         this.preventMark = restore;
35917         return v;
35918     },
35919
35920     /**
35921      * Validates the field value
35922      * @return {Boolean} True if the value is valid, else false
35923      */
35924     validate : function(){
35925         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35926             this.clearInvalid();
35927             return true;
35928         }
35929         return false;
35930     },
35931
35932     processValue : function(value){
35933         return value;
35934     },
35935
35936     // private
35937     // Subclasses should provide the validation implementation by overriding this
35938     validateValue : function(value){
35939         return true;
35940     },
35941
35942     /**
35943      * Mark this field as invalid
35944      * @param {String} msg The validation message
35945      */
35946     markInvalid : function(msg){
35947         if(!this.rendered || this.preventMark){ // not rendered
35948             return;
35949         }
35950         this.el.addClass(this.invalidClass);
35951         msg = msg || this.invalidText;
35952         switch(this.msgTarget){
35953             case 'qtip':
35954                 this.el.dom.qtip = msg;
35955                 this.el.dom.qclass = 'x-form-invalid-tip';
35956                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35957                     Roo.QuickTips.enable();
35958                 }
35959                 break;
35960             case 'title':
35961                 this.el.dom.title = msg;
35962                 break;
35963             case 'under':
35964                 if(!this.errorEl){
35965                     var elp = this.el.findParent('.x-form-element', 5, true);
35966                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35967                     this.errorEl.setWidth(elp.getWidth(true)-20);
35968                 }
35969                 this.errorEl.update(msg);
35970                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35971                 break;
35972             case 'side':
35973                 if(!this.errorIcon){
35974                     var elp = this.el.findParent('.x-form-element', 5, true);
35975                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35976                 }
35977                 this.alignErrorIcon();
35978                 this.errorIcon.dom.qtip = msg;
35979                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35980                 this.errorIcon.show();
35981                 this.on('resize', this.alignErrorIcon, this);
35982                 break;
35983             default:
35984                 var t = Roo.getDom(this.msgTarget);
35985                 t.innerHTML = msg;
35986                 t.style.display = this.msgDisplay;
35987                 break;
35988         }
35989         this.fireEvent('invalid', this, msg);
35990     },
35991
35992     // private
35993     alignErrorIcon : function(){
35994         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35995     },
35996
35997     /**
35998      * Clear any invalid styles/messages for this field
35999      */
36000     clearInvalid : function(){
36001         if(!this.rendered || this.preventMark){ // not rendered
36002             return;
36003         }
36004         this.el.removeClass(this.invalidClass);
36005         switch(this.msgTarget){
36006             case 'qtip':
36007                 this.el.dom.qtip = '';
36008                 break;
36009             case 'title':
36010                 this.el.dom.title = '';
36011                 break;
36012             case 'under':
36013                 if(this.errorEl){
36014                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36015                 }
36016                 break;
36017             case 'side':
36018                 if(this.errorIcon){
36019                     this.errorIcon.dom.qtip = '';
36020                     this.errorIcon.hide();
36021                     this.un('resize', this.alignErrorIcon, this);
36022                 }
36023                 break;
36024             default:
36025                 var t = Roo.getDom(this.msgTarget);
36026                 t.innerHTML = '';
36027                 t.style.display = 'none';
36028                 break;
36029         }
36030         this.fireEvent('valid', this);
36031     },
36032
36033     /**
36034      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36035      * @return {Mixed} value The field value
36036      */
36037     getRawValue : function(){
36038         var v = this.el.getValue();
36039         if(v === this.emptyText){
36040             v = '';
36041         }
36042         return v;
36043     },
36044
36045     /**
36046      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36047      * @return {Mixed} value The field value
36048      */
36049     getValue : function(){
36050         var v = this.el.getValue();
36051         if(v === this.emptyText || v === undefined){
36052             v = '';
36053         }
36054         return v;
36055     },
36056
36057     /**
36058      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36059      * @param {Mixed} value The value to set
36060      */
36061     setRawValue : function(v){
36062         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36063     },
36064
36065     /**
36066      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36067      * @param {Mixed} value The value to set
36068      */
36069     setValue : function(v){
36070         this.value = v;
36071         if(this.rendered){
36072             this.el.dom.value = (v === null || v === undefined ? '' : v);
36073              this.validate();
36074         }
36075     },
36076
36077     adjustSize : function(w, h){
36078         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36079         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36080         return s;
36081     },
36082
36083     adjustWidth : function(tag, w){
36084         tag = tag.toLowerCase();
36085         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36086             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36087                 if(tag == 'input'){
36088                     return w + 2;
36089                 }
36090                 if(tag = 'textarea'){
36091                     return w-2;
36092                 }
36093             }else if(Roo.isOpera){
36094                 if(tag == 'input'){
36095                     return w + 2;
36096                 }
36097                 if(tag = 'textarea'){
36098                     return w-2;
36099                 }
36100             }
36101         }
36102         return w;
36103     }
36104 });
36105
36106
36107 // anything other than normal should be considered experimental
36108 Roo.form.Field.msgFx = {
36109     normal : {
36110         show: function(msgEl, f){
36111             msgEl.setDisplayed('block');
36112         },
36113
36114         hide : function(msgEl, f){
36115             msgEl.setDisplayed(false).update('');
36116         }
36117     },
36118
36119     slide : {
36120         show: function(msgEl, f){
36121             msgEl.slideIn('t', {stopFx:true});
36122         },
36123
36124         hide : function(msgEl, f){
36125             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36126         }
36127     },
36128
36129     slideRight : {
36130         show: function(msgEl, f){
36131             msgEl.fixDisplay();
36132             msgEl.alignTo(f.el, 'tl-tr');
36133             msgEl.slideIn('l', {stopFx:true});
36134         },
36135
36136         hide : function(msgEl, f){
36137             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36138         }
36139     }
36140 };/*
36141  * Based on:
36142  * Ext JS Library 1.1.1
36143  * Copyright(c) 2006-2007, Ext JS, LLC.
36144  *
36145  * Originally Released Under LGPL - original licence link has changed is not relivant.
36146  *
36147  * Fork - LGPL
36148  * <script type="text/javascript">
36149  */
36150  
36151
36152 /**
36153  * @class Roo.form.TextField
36154  * @extends Roo.form.Field
36155  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36156  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36157  * @constructor
36158  * Creates a new TextField
36159  * @param {Object} config Configuration options
36160  */
36161 Roo.form.TextField = function(config){
36162     Roo.form.TextField.superclass.constructor.call(this, config);
36163     this.addEvents({
36164         /**
36165          * @event autosize
36166          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36167          * according to the default logic, but this event provides a hook for the developer to apply additional
36168          * logic at runtime to resize the field if needed.
36169              * @param {Roo.form.Field} this This text field
36170              * @param {Number} width The new field width
36171              */
36172         autosize : true
36173     });
36174 };
36175
36176 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36177     /**
36178      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36179      */
36180     grow : false,
36181     /**
36182      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36183      */
36184     growMin : 30,
36185     /**
36186      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36187      */
36188     growMax : 800,
36189     /**
36190      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36191      */
36192     vtype : null,
36193     /**
36194      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36195      */
36196     maskRe : null,
36197     /**
36198      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36199      */
36200     disableKeyFilter : false,
36201     /**
36202      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36203      */
36204     allowBlank : true,
36205     /**
36206      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36207      */
36208     minLength : 0,
36209     /**
36210      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36211      */
36212     maxLength : Number.MAX_VALUE,
36213     /**
36214      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36215      */
36216     minLengthText : "The minimum length for this field is {0}",
36217     /**
36218      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36219      */
36220     maxLengthText : "The maximum length for this field is {0}",
36221     /**
36222      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36223      */
36224     selectOnFocus : false,
36225     /**
36226      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36227      */
36228     blankText : "This field is required",
36229     /**
36230      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36231      * If available, this function will be called only after the basic validators all return true, and will be passed the
36232      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36233      */
36234     validator : null,
36235     /**
36236      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36237      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36238      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36239      */
36240     regex : null,
36241     /**
36242      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36243      */
36244     regexText : "",
36245     /**
36246      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36247      */
36248     emptyText : null,
36249     /**
36250      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36251      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36252      */
36253     emptyClass : 'x-form-empty-field',
36254
36255     // private
36256     initEvents : function(){
36257         Roo.form.TextField.superclass.initEvents.call(this);
36258         if(this.validationEvent == 'keyup'){
36259             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36260             this.el.on('keyup', this.filterValidation, this);
36261         }
36262         else if(this.validationEvent !== false){
36263             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36264         }
36265         if(this.selectOnFocus || this.emptyText){
36266             this.on("focus", this.preFocus, this);
36267             if(this.emptyText){
36268                 this.on('blur', this.postBlur, this);
36269                 this.applyEmptyText();
36270             }
36271         }
36272         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36273             this.el.on("keypress", this.filterKeys, this);
36274         }
36275         if(this.grow){
36276             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36277             this.el.on("click", this.autoSize,  this);
36278         }
36279     },
36280
36281     processValue : function(value){
36282         if(this.stripCharsRe){
36283             var newValue = value.replace(this.stripCharsRe, '');
36284             if(newValue !== value){
36285                 this.setRawValue(newValue);
36286                 return newValue;
36287             }
36288         }
36289         return value;
36290     },
36291
36292     filterValidation : function(e){
36293         if(!e.isNavKeyPress()){
36294             this.validationTask.delay(this.validationDelay);
36295         }
36296     },
36297
36298     // private
36299     onKeyUp : function(e){
36300         if(!e.isNavKeyPress()){
36301             this.autoSize();
36302         }
36303     },
36304
36305     /**
36306      * Resets the current field value to the originally-loaded value and clears any validation messages.
36307      * Also adds emptyText and emptyClass if the original value was blank.
36308      */
36309     reset : function(){
36310         Roo.form.TextField.superclass.reset.call(this);
36311         this.applyEmptyText();
36312     },
36313
36314     applyEmptyText : function(){
36315         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36316             this.setRawValue(this.emptyText);
36317             this.el.addClass(this.emptyClass);
36318         }
36319     },
36320
36321     // private
36322     preFocus : function(){
36323         if(this.emptyText){
36324             if(this.el.dom.value == this.emptyText){
36325                 this.setRawValue('');
36326             }
36327             this.el.removeClass(this.emptyClass);
36328         }
36329         if(this.selectOnFocus){
36330             this.el.dom.select();
36331         }
36332     },
36333
36334     // private
36335     postBlur : function(){
36336         this.applyEmptyText();
36337     },
36338
36339     // private
36340     filterKeys : function(e){
36341         var k = e.getKey();
36342         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36343             return;
36344         }
36345         var c = e.getCharCode(), cc = String.fromCharCode(c);
36346         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36347             return;
36348         }
36349         if(!this.maskRe.test(cc)){
36350             e.stopEvent();
36351         }
36352     },
36353
36354     setValue : function(v){
36355         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36356             this.el.removeClass(this.emptyClass);
36357         }
36358         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36359         this.applyEmptyText();
36360         this.autoSize();
36361     },
36362
36363     /**
36364      * Validates a value according to the field's validation rules and marks the field as invalid
36365      * if the validation fails
36366      * @param {Mixed} value The value to validate
36367      * @return {Boolean} True if the value is valid, else false
36368      */
36369     validateValue : function(value){
36370         if(value.length < 1 || value === this.emptyText){ // if it's blank
36371              if(this.allowBlank){
36372                 this.clearInvalid();
36373                 return true;
36374              }else{
36375                 this.markInvalid(this.blankText);
36376                 return false;
36377              }
36378         }
36379         if(value.length < this.minLength){
36380             this.markInvalid(String.format(this.minLengthText, this.minLength));
36381             return false;
36382         }
36383         if(value.length > this.maxLength){
36384             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36385             return false;
36386         }
36387         if(this.vtype){
36388             var vt = Roo.form.VTypes;
36389             if(!vt[this.vtype](value, this)){
36390                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36391                 return false;
36392             }
36393         }
36394         if(typeof this.validator == "function"){
36395             var msg = this.validator(value);
36396             if(msg !== true){
36397                 this.markInvalid(msg);
36398                 return false;
36399             }
36400         }
36401         if(this.regex && !this.regex.test(value)){
36402             this.markInvalid(this.regexText);
36403             return false;
36404         }
36405         return true;
36406     },
36407
36408     /**
36409      * Selects text in this field
36410      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36411      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36412      */
36413     selectText : function(start, end){
36414         var v = this.getRawValue();
36415         if(v.length > 0){
36416             start = start === undefined ? 0 : start;
36417             end = end === undefined ? v.length : end;
36418             var d = this.el.dom;
36419             if(d.setSelectionRange){
36420                 d.setSelectionRange(start, end);
36421             }else if(d.createTextRange){
36422                 var range = d.createTextRange();
36423                 range.moveStart("character", start);
36424                 range.moveEnd("character", v.length-end);
36425                 range.select();
36426             }
36427         }
36428     },
36429
36430     /**
36431      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36432      * This only takes effect if grow = true, and fires the autosize event.
36433      */
36434     autoSize : function(){
36435         if(!this.grow || !this.rendered){
36436             return;
36437         }
36438         if(!this.metrics){
36439             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36440         }
36441         var el = this.el;
36442         var v = el.dom.value;
36443         var d = document.createElement('div');
36444         d.appendChild(document.createTextNode(v));
36445         v = d.innerHTML;
36446         d = null;
36447         v += "&#160;";
36448         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36449         this.el.setWidth(w);
36450         this.fireEvent("autosize", this, w);
36451     }
36452 });/*
36453  * Based on:
36454  * Ext JS Library 1.1.1
36455  * Copyright(c) 2006-2007, Ext JS, LLC.
36456  *
36457  * Originally Released Under LGPL - original licence link has changed is not relivant.
36458  *
36459  * Fork - LGPL
36460  * <script type="text/javascript">
36461  */
36462  
36463 /**
36464  * @class Roo.form.Hidden
36465  * @extends Roo.form.TextField
36466  * Simple Hidden element used on forms 
36467  * 
36468  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36469  * 
36470  * @constructor
36471  * Creates a new Hidden form element.
36472  * @param {Object} config Configuration options
36473  */
36474
36475
36476
36477 // easy hidden field...
36478 Roo.form.Hidden = function(config){
36479     Roo.form.Hidden.superclass.constructor.call(this, config);
36480 };
36481   
36482 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36483     fieldLabel:      '',
36484     inputType:      'hidden',
36485     width:          50,
36486     allowBlank:     true,
36487     labelSeparator: '',
36488     hidden:         true,
36489     itemCls :       'x-form-item-display-none'
36490
36491
36492 });
36493
36494
36495 /*
36496  * Based on:
36497  * Ext JS Library 1.1.1
36498  * Copyright(c) 2006-2007, Ext JS, LLC.
36499  *
36500  * Originally Released Under LGPL - original licence link has changed is not relivant.
36501  *
36502  * Fork - LGPL
36503  * <script type="text/javascript">
36504  */
36505  
36506 /**
36507  * @class Roo.form.TriggerField
36508  * @extends Roo.form.TextField
36509  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36510  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36511  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36512  * for which you can provide a custom implementation.  For example:
36513  * <pre><code>
36514 var trigger = new Roo.form.TriggerField();
36515 trigger.onTriggerClick = myTriggerFn;
36516 trigger.applyTo('my-field');
36517 </code></pre>
36518  *
36519  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36520  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36521  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36522  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36523  * @constructor
36524  * Create a new TriggerField.
36525  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36526  * to the base TextField)
36527  */
36528 Roo.form.TriggerField = function(config){
36529     this.mimicing = false;
36530     Roo.form.TriggerField.superclass.constructor.call(this, config);
36531 };
36532
36533 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36534     /**
36535      * @cfg {String} triggerClass A CSS class to apply to the trigger
36536      */
36537     /**
36538      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36539      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36540      */
36541     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36542     /**
36543      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36544      */
36545     hideTrigger:false,
36546
36547     /** @cfg {Boolean} grow @hide */
36548     /** @cfg {Number} growMin @hide */
36549     /** @cfg {Number} growMax @hide */
36550
36551     /**
36552      * @hide 
36553      * @method
36554      */
36555     autoSize: Roo.emptyFn,
36556     // private
36557     monitorTab : true,
36558     // private
36559     deferHeight : true,
36560
36561     
36562     actionMode : 'wrap',
36563     // private
36564     onResize : function(w, h){
36565         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36566         if(typeof w == 'number'){
36567             var x = w - this.trigger.getWidth();
36568             this.el.setWidth(this.adjustWidth('input', x));
36569             this.trigger.setStyle('left', x+'px');
36570         }
36571     },
36572
36573     // private
36574     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36575
36576     // private
36577     getResizeEl : function(){
36578         return this.wrap;
36579     },
36580
36581     // private
36582     getPositionEl : function(){
36583         return this.wrap;
36584     },
36585
36586     // private
36587     alignErrorIcon : function(){
36588         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36589     },
36590
36591     // private
36592     onRender : function(ct, position){
36593         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36594         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36595         this.trigger = this.wrap.createChild(this.triggerConfig ||
36596                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36597         if(this.hideTrigger){
36598             this.trigger.setDisplayed(false);
36599         }
36600         this.initTrigger();
36601         if(!this.width){
36602             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36603         }
36604     },
36605
36606     // private
36607     initTrigger : function(){
36608         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36609         this.trigger.addClassOnOver('x-form-trigger-over');
36610         this.trigger.addClassOnClick('x-form-trigger-click');
36611     },
36612
36613     // private
36614     onDestroy : function(){
36615         if(this.trigger){
36616             this.trigger.removeAllListeners();
36617             this.trigger.remove();
36618         }
36619         if(this.wrap){
36620             this.wrap.remove();
36621         }
36622         Roo.form.TriggerField.superclass.onDestroy.call(this);
36623     },
36624
36625     // private
36626     onFocus : function(){
36627         Roo.form.TriggerField.superclass.onFocus.call(this);
36628         if(!this.mimicing){
36629             this.wrap.addClass('x-trigger-wrap-focus');
36630             this.mimicing = true;
36631             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36632             if(this.monitorTab){
36633                 this.el.on("keydown", this.checkTab, this);
36634             }
36635         }
36636     },
36637
36638     // private
36639     checkTab : function(e){
36640         if(e.getKey() == e.TAB){
36641             this.triggerBlur();
36642         }
36643     },
36644
36645     // private
36646     onBlur : function(){
36647         // do nothing
36648     },
36649
36650     // private
36651     mimicBlur : function(e, t){
36652         if(!this.wrap.contains(t) && this.validateBlur()){
36653             this.triggerBlur();
36654         }
36655     },
36656
36657     // private
36658     triggerBlur : function(){
36659         this.mimicing = false;
36660         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36661         if(this.monitorTab){
36662             this.el.un("keydown", this.checkTab, this);
36663         }
36664         this.wrap.removeClass('x-trigger-wrap-focus');
36665         Roo.form.TriggerField.superclass.onBlur.call(this);
36666     },
36667
36668     // private
36669     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36670     validateBlur : function(e, t){
36671         return true;
36672     },
36673
36674     // private
36675     onDisable : function(){
36676         Roo.form.TriggerField.superclass.onDisable.call(this);
36677         if(this.wrap){
36678             this.wrap.addClass('x-item-disabled');
36679         }
36680     },
36681
36682     // private
36683     onEnable : function(){
36684         Roo.form.TriggerField.superclass.onEnable.call(this);
36685         if(this.wrap){
36686             this.wrap.removeClass('x-item-disabled');
36687         }
36688     },
36689
36690     // private
36691     onShow : function(){
36692         var ae = this.getActionEl();
36693         
36694         if(ae){
36695             ae.dom.style.display = '';
36696             ae.dom.style.visibility = 'visible';
36697         }
36698     },
36699
36700     // private
36701     
36702     onHide : function(){
36703         var ae = this.getActionEl();
36704         ae.dom.style.display = 'none';
36705     },
36706
36707     /**
36708      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36709      * by an implementing function.
36710      * @method
36711      * @param {EventObject} e
36712      */
36713     onTriggerClick : Roo.emptyFn
36714 });
36715
36716 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36717 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36718 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36719 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36720     initComponent : function(){
36721         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36722
36723         this.triggerConfig = {
36724             tag:'span', cls:'x-form-twin-triggers', cn:[
36725             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36726             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36727         ]};
36728     },
36729
36730     getTrigger : function(index){
36731         return this.triggers[index];
36732     },
36733
36734     initTrigger : function(){
36735         var ts = this.trigger.select('.x-form-trigger', true);
36736         this.wrap.setStyle('overflow', 'hidden');
36737         var triggerField = this;
36738         ts.each(function(t, all, index){
36739             t.hide = function(){
36740                 var w = triggerField.wrap.getWidth();
36741                 this.dom.style.display = 'none';
36742                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36743             };
36744             t.show = function(){
36745                 var w = triggerField.wrap.getWidth();
36746                 this.dom.style.display = '';
36747                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36748             };
36749             var triggerIndex = 'Trigger'+(index+1);
36750
36751             if(this['hide'+triggerIndex]){
36752                 t.dom.style.display = 'none';
36753             }
36754             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36755             t.addClassOnOver('x-form-trigger-over');
36756             t.addClassOnClick('x-form-trigger-click');
36757         }, this);
36758         this.triggers = ts.elements;
36759     },
36760
36761     onTrigger1Click : Roo.emptyFn,
36762     onTrigger2Click : Roo.emptyFn
36763 });/*
36764  * Based on:
36765  * Ext JS Library 1.1.1
36766  * Copyright(c) 2006-2007, Ext JS, LLC.
36767  *
36768  * Originally Released Under LGPL - original licence link has changed is not relivant.
36769  *
36770  * Fork - LGPL
36771  * <script type="text/javascript">
36772  */
36773  
36774 /**
36775  * @class Roo.form.TextArea
36776  * @extends Roo.form.TextField
36777  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36778  * support for auto-sizing.
36779  * @constructor
36780  * Creates a new TextArea
36781  * @param {Object} config Configuration options
36782  */
36783 Roo.form.TextArea = function(config){
36784     Roo.form.TextArea.superclass.constructor.call(this, config);
36785     // these are provided exchanges for backwards compat
36786     // minHeight/maxHeight were replaced by growMin/growMax to be
36787     // compatible with TextField growing config values
36788     if(this.minHeight !== undefined){
36789         this.growMin = this.minHeight;
36790     }
36791     if(this.maxHeight !== undefined){
36792         this.growMax = this.maxHeight;
36793     }
36794 };
36795
36796 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36797     /**
36798      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36799      */
36800     growMin : 60,
36801     /**
36802      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36803      */
36804     growMax: 1000,
36805     /**
36806      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36807      * in the field (equivalent to setting overflow: hidden, defaults to false)
36808      */
36809     preventScrollbars: false,
36810     /**
36811      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36812      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36813      */
36814
36815     // private
36816     onRender : function(ct, position){
36817         if(!this.el){
36818             this.defaultAutoCreate = {
36819                 tag: "textarea",
36820                 style:"width:300px;height:60px;",
36821                 autocomplete: "off"
36822             };
36823         }
36824         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36825         if(this.grow){
36826             this.textSizeEl = Roo.DomHelper.append(document.body, {
36827                 tag: "pre", cls: "x-form-grow-sizer"
36828             });
36829             if(this.preventScrollbars){
36830                 this.el.setStyle("overflow", "hidden");
36831             }
36832             this.el.setHeight(this.growMin);
36833         }
36834     },
36835
36836     onDestroy : function(){
36837         if(this.textSizeEl){
36838             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36839         }
36840         Roo.form.TextArea.superclass.onDestroy.call(this);
36841     },
36842
36843     // private
36844     onKeyUp : function(e){
36845         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36846             this.autoSize();
36847         }
36848     },
36849
36850     /**
36851      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36852      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36853      */
36854     autoSize : function(){
36855         if(!this.grow || !this.textSizeEl){
36856             return;
36857         }
36858         var el = this.el;
36859         var v = el.dom.value;
36860         var ts = this.textSizeEl;
36861
36862         ts.innerHTML = '';
36863         ts.appendChild(document.createTextNode(v));
36864         v = ts.innerHTML;
36865
36866         Roo.fly(ts).setWidth(this.el.getWidth());
36867         if(v.length < 1){
36868             v = "&#160;&#160;";
36869         }else{
36870             if(Roo.isIE){
36871                 v = v.replace(/\n/g, '<p>&#160;</p>');
36872             }
36873             v += "&#160;\n&#160;";
36874         }
36875         ts.innerHTML = v;
36876         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36877         if(h != this.lastHeight){
36878             this.lastHeight = h;
36879             this.el.setHeight(h);
36880             this.fireEvent("autosize", this, h);
36881         }
36882     }
36883 });/*
36884  * Based on:
36885  * Ext JS Library 1.1.1
36886  * Copyright(c) 2006-2007, Ext JS, LLC.
36887  *
36888  * Originally Released Under LGPL - original licence link has changed is not relivant.
36889  *
36890  * Fork - LGPL
36891  * <script type="text/javascript">
36892  */
36893  
36894
36895 /**
36896  * @class Roo.form.NumberField
36897  * @extends Roo.form.TextField
36898  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36899  * @constructor
36900  * Creates a new NumberField
36901  * @param {Object} config Configuration options
36902  */
36903 Roo.form.NumberField = function(config){
36904     Roo.form.NumberField.superclass.constructor.call(this, config);
36905 };
36906
36907 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36908     /**
36909      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36910      */
36911     fieldClass: "x-form-field x-form-num-field",
36912     /**
36913      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36914      */
36915     allowDecimals : true,
36916     /**
36917      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36918      */
36919     decimalSeparator : ".",
36920     /**
36921      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36922      */
36923     decimalPrecision : 2,
36924     /**
36925      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36926      */
36927     allowNegative : true,
36928     /**
36929      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36930      */
36931     minValue : Number.NEGATIVE_INFINITY,
36932     /**
36933      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36934      */
36935     maxValue : Number.MAX_VALUE,
36936     /**
36937      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36938      */
36939     minText : "The minimum value for this field is {0}",
36940     /**
36941      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36942      */
36943     maxText : "The maximum value for this field is {0}",
36944     /**
36945      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36946      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36947      */
36948     nanText : "{0} is not a valid number",
36949
36950     // private
36951     initEvents : function(){
36952         Roo.form.NumberField.superclass.initEvents.call(this);
36953         var allowed = "0123456789";
36954         if(this.allowDecimals){
36955             allowed += this.decimalSeparator;
36956         }
36957         if(this.allowNegative){
36958             allowed += "-";
36959         }
36960         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36961         var keyPress = function(e){
36962             var k = e.getKey();
36963             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36964                 return;
36965             }
36966             var c = e.getCharCode();
36967             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36968                 e.stopEvent();
36969             }
36970         };
36971         this.el.on("keypress", keyPress, this);
36972     },
36973
36974     // private
36975     validateValue : function(value){
36976         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36977             return false;
36978         }
36979         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36980              return true;
36981         }
36982         var num = this.parseValue(value);
36983         if(isNaN(num)){
36984             this.markInvalid(String.format(this.nanText, value));
36985             return false;
36986         }
36987         if(num < this.minValue){
36988             this.markInvalid(String.format(this.minText, this.minValue));
36989             return false;
36990         }
36991         if(num > this.maxValue){
36992             this.markInvalid(String.format(this.maxText, this.maxValue));
36993             return false;
36994         }
36995         return true;
36996     },
36997
36998     getValue : function(){
36999         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37000     },
37001
37002     // private
37003     parseValue : function(value){
37004         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37005         return isNaN(value) ? '' : value;
37006     },
37007
37008     // private
37009     fixPrecision : function(value){
37010         var nan = isNaN(value);
37011         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37012             return nan ? '' : value;
37013         }
37014         return parseFloat(value).toFixed(this.decimalPrecision);
37015     },
37016
37017     setValue : function(v){
37018         v = this.fixPrecision(v);
37019         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37020     },
37021
37022     // private
37023     decimalPrecisionFcn : function(v){
37024         return Math.floor(v);
37025     },
37026
37027     beforeBlur : function(){
37028         var v = this.parseValue(this.getRawValue());
37029         if(v){
37030             this.setValue(v);
37031         }
37032     }
37033 });/*
37034  * Based on:
37035  * Ext JS Library 1.1.1
37036  * Copyright(c) 2006-2007, Ext JS, LLC.
37037  *
37038  * Originally Released Under LGPL - original licence link has changed is not relivant.
37039  *
37040  * Fork - LGPL
37041  * <script type="text/javascript">
37042  */
37043  
37044 /**
37045  * @class Roo.form.DateField
37046  * @extends Roo.form.TriggerField
37047  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37048 * @constructor
37049 * Create a new DateField
37050 * @param {Object} config
37051  */
37052 Roo.form.DateField = function(config){
37053     Roo.form.DateField.superclass.constructor.call(this, config);
37054     
37055       this.addEvents({
37056          
37057         /**
37058          * @event select
37059          * Fires when a date is selected
37060              * @param {Roo.form.DateField} combo This combo box
37061              * @param {Date} date The date selected
37062              */
37063         'select' : true
37064          
37065     });
37066     
37067     
37068     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37069     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37070     this.ddMatch = null;
37071     if(this.disabledDates){
37072         var dd = this.disabledDates;
37073         var re = "(?:";
37074         for(var i = 0; i < dd.length; i++){
37075             re += dd[i];
37076             if(i != dd.length-1) re += "|";
37077         }
37078         this.ddMatch = new RegExp(re + ")");
37079     }
37080 };
37081
37082 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37083     /**
37084      * @cfg {String} format
37085      * The default date format string which can be overriden for localization support.  The format must be
37086      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37087      */
37088     format : "m/d/y",
37089     /**
37090      * @cfg {String} altFormats
37091      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37092      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37093      */
37094     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37095     /**
37096      * @cfg {Array} disabledDays
37097      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37098      */
37099     disabledDays : null,
37100     /**
37101      * @cfg {String} disabledDaysText
37102      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37103      */
37104     disabledDaysText : "Disabled",
37105     /**
37106      * @cfg {Array} disabledDates
37107      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37108      * expression so they are very powerful. Some examples:
37109      * <ul>
37110      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37111      * <li>["03/08", "09/16"] would disable those days for every year</li>
37112      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37113      * <li>["03/../2006"] would disable every day in March 2006</li>
37114      * <li>["^03"] would disable every day in every March</li>
37115      * </ul>
37116      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37117      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37118      */
37119     disabledDates : null,
37120     /**
37121      * @cfg {String} disabledDatesText
37122      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37123      */
37124     disabledDatesText : "Disabled",
37125     /**
37126      * @cfg {Date/String} minValue
37127      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37128      * valid format (defaults to null).
37129      */
37130     minValue : null,
37131     /**
37132      * @cfg {Date/String} maxValue
37133      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37134      * valid format (defaults to null).
37135      */
37136     maxValue : null,
37137     /**
37138      * @cfg {String} minText
37139      * The error text to display when the date in the cell is before minValue (defaults to
37140      * 'The date in this field must be after {minValue}').
37141      */
37142     minText : "The date in this field must be equal to or after {0}",
37143     /**
37144      * @cfg {String} maxText
37145      * The error text to display when the date in the cell is after maxValue (defaults to
37146      * 'The date in this field must be before {maxValue}').
37147      */
37148     maxText : "The date in this field must be equal to or before {0}",
37149     /**
37150      * @cfg {String} invalidText
37151      * The error text to display when the date in the field is invalid (defaults to
37152      * '{value} is not a valid date - it must be in the format {format}').
37153      */
37154     invalidText : "{0} is not a valid date - it must be in the format {1}",
37155     /**
37156      * @cfg {String} triggerClass
37157      * An additional CSS class used to style the trigger button.  The trigger will always get the
37158      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37159      * which displays a calendar icon).
37160      */
37161     triggerClass : 'x-form-date-trigger',
37162     
37163
37164     /**
37165      * @cfg {bool} useIso
37166      * if enabled, then the date field will use a hidden field to store the 
37167      * real value as iso formated date. default (false)
37168      */ 
37169     useIso : false,
37170     /**
37171      * @cfg {String/Object} autoCreate
37172      * A DomHelper element spec, or true for a default element spec (defaults to
37173      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37174      */ 
37175     // private
37176     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37177     
37178     // private
37179     hiddenField: false,
37180     
37181     onRender : function(ct, position)
37182     {
37183         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37184         if (this.useIso) {
37185             this.el.dom.removeAttribute('name'); 
37186             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37187                     'before', true);
37188             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37189             // prevent input submission
37190             this.hiddenName = this.name;
37191         }
37192             
37193             
37194     },
37195     
37196     // private
37197     validateValue : function(value)
37198     {
37199         value = this.formatDate(value);
37200         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37201             return false;
37202         }
37203         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37204              return true;
37205         }
37206         var svalue = value;
37207         value = this.parseDate(value);
37208         if(!value){
37209             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37210             return false;
37211         }
37212         var time = value.getTime();
37213         if(this.minValue && time < this.minValue.getTime()){
37214             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37215             return false;
37216         }
37217         if(this.maxValue && time > this.maxValue.getTime()){
37218             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37219             return false;
37220         }
37221         if(this.disabledDays){
37222             var day = value.getDay();
37223             for(var i = 0; i < this.disabledDays.length; i++) {
37224                 if(day === this.disabledDays[i]){
37225                     this.markInvalid(this.disabledDaysText);
37226                     return false;
37227                 }
37228             }
37229         }
37230         var fvalue = this.formatDate(value);
37231         if(this.ddMatch && this.ddMatch.test(fvalue)){
37232             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37233             return false;
37234         }
37235         return true;
37236     },
37237
37238     // private
37239     // Provides logic to override the default TriggerField.validateBlur which just returns true
37240     validateBlur : function(){
37241         return !this.menu || !this.menu.isVisible();
37242     },
37243
37244     /**
37245      * Returns the current date value of the date field.
37246      * @return {Date} The date value
37247      */
37248     getValue : function(){
37249         
37250         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37251     },
37252
37253     /**
37254      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37255      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37256      * (the default format used is "m/d/y").
37257      * <br />Usage:
37258      * <pre><code>
37259 //All of these calls set the same date value (May 4, 2006)
37260
37261 //Pass a date object:
37262 var dt = new Date('5/4/06');
37263 dateField.setValue(dt);
37264
37265 //Pass a date string (default format):
37266 dateField.setValue('5/4/06');
37267
37268 //Pass a date string (custom format):
37269 dateField.format = 'Y-m-d';
37270 dateField.setValue('2006-5-4');
37271 </code></pre>
37272      * @param {String/Date} date The date or valid date string
37273      */
37274     setValue : function(date){
37275         if (this.hiddenField) {
37276             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37277         }
37278         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37279     },
37280
37281     // private
37282     parseDate : function(value){
37283         if(!value || value instanceof Date){
37284             return value;
37285         }
37286         var v = Date.parseDate(value, this.format);
37287         if(!v && this.altFormats){
37288             if(!this.altFormatsArray){
37289                 this.altFormatsArray = this.altFormats.split("|");
37290             }
37291             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37292                 v = Date.parseDate(value, this.altFormatsArray[i]);
37293             }
37294         }
37295         return v;
37296     },
37297
37298     // private
37299     formatDate : function(date, fmt){
37300         return (!date || !(date instanceof Date)) ?
37301                date : date.dateFormat(fmt || this.format);
37302     },
37303
37304     // private
37305     menuListeners : {
37306         select: function(m, d){
37307             this.setValue(d);
37308             this.fireEvent('select', this, d);
37309         },
37310         show : function(){ // retain focus styling
37311             this.onFocus();
37312         },
37313         hide : function(){
37314             this.focus.defer(10, this);
37315             var ml = this.menuListeners;
37316             this.menu.un("select", ml.select,  this);
37317             this.menu.un("show", ml.show,  this);
37318             this.menu.un("hide", ml.hide,  this);
37319         }
37320     },
37321
37322     // private
37323     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37324     onTriggerClick : function(){
37325         if(this.disabled){
37326             return;
37327         }
37328         if(this.menu == null){
37329             this.menu = new Roo.menu.DateMenu();
37330         }
37331         Roo.apply(this.menu.picker,  {
37332             showClear: this.allowBlank,
37333             minDate : this.minValue,
37334             maxDate : this.maxValue,
37335             disabledDatesRE : this.ddMatch,
37336             disabledDatesText : this.disabledDatesText,
37337             disabledDays : this.disabledDays,
37338             disabledDaysText : this.disabledDaysText,
37339             format : this.format,
37340             minText : String.format(this.minText, this.formatDate(this.minValue)),
37341             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37342         });
37343         this.menu.on(Roo.apply({}, this.menuListeners, {
37344             scope:this
37345         }));
37346         this.menu.picker.setValue(this.getValue() || new Date());
37347         this.menu.show(this.el, "tl-bl?");
37348     },
37349
37350     beforeBlur : function(){
37351         var v = this.parseDate(this.getRawValue());
37352         if(v){
37353             this.setValue(v);
37354         }
37355     }
37356
37357     /** @cfg {Boolean} grow @hide */
37358     /** @cfg {Number} growMin @hide */
37359     /** @cfg {Number} growMax @hide */
37360     /**
37361      * @hide
37362      * @method autoSize
37363      */
37364 });/*
37365  * Based on:
37366  * Ext JS Library 1.1.1
37367  * Copyright(c) 2006-2007, Ext JS, LLC.
37368  *
37369  * Originally Released Under LGPL - original licence link has changed is not relivant.
37370  *
37371  * Fork - LGPL
37372  * <script type="text/javascript">
37373  */
37374  
37375
37376 /**
37377  * @class Roo.form.ComboBox
37378  * @extends Roo.form.TriggerField
37379  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37380  * @constructor
37381  * Create a new ComboBox.
37382  * @param {Object} config Configuration options
37383  */
37384 Roo.form.ComboBox = function(config){
37385     Roo.form.ComboBox.superclass.constructor.call(this, config);
37386     this.addEvents({
37387         /**
37388          * @event expand
37389          * Fires when the dropdown list is expanded
37390              * @param {Roo.form.ComboBox} combo This combo box
37391              */
37392         'expand' : true,
37393         /**
37394          * @event collapse
37395          * Fires when the dropdown list is collapsed
37396              * @param {Roo.form.ComboBox} combo This combo box
37397              */
37398         'collapse' : true,
37399         /**
37400          * @event beforeselect
37401          * Fires before a list item is selected. Return false to cancel the selection.
37402              * @param {Roo.form.ComboBox} combo This combo box
37403              * @param {Roo.data.Record} record The data record returned from the underlying store
37404              * @param {Number} index The index of the selected item in the dropdown list
37405              */
37406         'beforeselect' : true,
37407         /**
37408          * @event select
37409          * Fires when a list item is selected
37410              * @param {Roo.form.ComboBox} combo This combo box
37411              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37412              * @param {Number} index The index of the selected item in the dropdown list
37413              */
37414         'select' : true,
37415         /**
37416          * @event beforequery
37417          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37418          * The event object passed has these properties:
37419              * @param {Roo.form.ComboBox} combo This combo box
37420              * @param {String} query The query
37421              * @param {Boolean} forceAll true to force "all" query
37422              * @param {Boolean} cancel true to cancel the query
37423              * @param {Object} e The query event object
37424              */
37425         'beforequery': true,
37426          /**
37427          * @event add
37428          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37429              * @param {Roo.form.ComboBox} combo This combo box
37430              */
37431         'add' : true,
37432         /**
37433          * @event edit
37434          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37435              * @param {Roo.form.ComboBox} combo This combo box
37436              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37437              */
37438         'edit' : true
37439         
37440         
37441     });
37442     if(this.transform){
37443         this.allowDomMove = false;
37444         var s = Roo.getDom(this.transform);
37445         if(!this.hiddenName){
37446             this.hiddenName = s.name;
37447         }
37448         if(!this.store){
37449             this.mode = 'local';
37450             var d = [], opts = s.options;
37451             for(var i = 0, len = opts.length;i < len; i++){
37452                 var o = opts[i];
37453                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37454                 if(o.selected) {
37455                     this.value = value;
37456                 }
37457                 d.push([value, o.text]);
37458             }
37459             this.store = new Roo.data.SimpleStore({
37460                 'id': 0,
37461                 fields: ['value', 'text'],
37462                 data : d
37463             });
37464             this.valueField = 'value';
37465             this.displayField = 'text';
37466         }
37467         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37468         if(!this.lazyRender){
37469             this.target = true;
37470             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37471             s.parentNode.removeChild(s); // remove it
37472             this.render(this.el.parentNode);
37473         }else{
37474             s.parentNode.removeChild(s); // remove it
37475         }
37476
37477     }
37478     if (this.store) {
37479         this.store = Roo.factory(this.store, Roo.data);
37480     }
37481     
37482     this.selectedIndex = -1;
37483     if(this.mode == 'local'){
37484         if(config.queryDelay === undefined){
37485             this.queryDelay = 10;
37486         }
37487         if(config.minChars === undefined){
37488             this.minChars = 0;
37489         }
37490     }
37491 };
37492
37493 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37494     /**
37495      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37496      */
37497     /**
37498      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37499      * rendering into an Roo.Editor, defaults to false)
37500      */
37501     /**
37502      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37503      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37504      */
37505     /**
37506      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37507      */
37508     /**
37509      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37510      * the dropdown list (defaults to undefined, with no header element)
37511      */
37512
37513      /**
37514      * @cfg {String/Roo.Template} tpl The template to use to render the output
37515      */
37516      
37517     // private
37518     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37519     /**
37520      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37521      */
37522     listWidth: undefined,
37523     /**
37524      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37525      * mode = 'remote' or 'text' if mode = 'local')
37526      */
37527     displayField: undefined,
37528     /**
37529      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37530      * mode = 'remote' or 'value' if mode = 'local'). 
37531      * Note: use of a valueField requires the user make a selection
37532      * in order for a value to be mapped.
37533      */
37534     valueField: undefined,
37535     
37536     
37537     /**
37538      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37539      * field's data value (defaults to the underlying DOM element's name)
37540      */
37541     hiddenName: undefined,
37542     /**
37543      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37544      */
37545     listClass: '',
37546     /**
37547      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37548      */
37549     selectedClass: 'x-combo-selected',
37550     /**
37551      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37552      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37553      * which displays a downward arrow icon).
37554      */
37555     triggerClass : 'x-form-arrow-trigger',
37556     /**
37557      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37558      */
37559     shadow:'sides',
37560     /**
37561      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37562      * anchor positions (defaults to 'tl-bl')
37563      */
37564     listAlign: 'tl-bl?',
37565     /**
37566      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37567      */
37568     maxHeight: 300,
37569     /**
37570      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37571      * query specified by the allQuery config option (defaults to 'query')
37572      */
37573     triggerAction: 'query',
37574     /**
37575      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37576      * (defaults to 4, does not apply if editable = false)
37577      */
37578     minChars : 4,
37579     /**
37580      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37581      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37582      */
37583     typeAhead: false,
37584     /**
37585      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37586      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37587      */
37588     queryDelay: 500,
37589     /**
37590      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37591      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37592      */
37593     pageSize: 0,
37594     /**
37595      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37596      * when editable = true (defaults to false)
37597      */
37598     selectOnFocus:false,
37599     /**
37600      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37601      */
37602     queryParam: 'query',
37603     /**
37604      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37605      * when mode = 'remote' (defaults to 'Loading...')
37606      */
37607     loadingText: 'Loading...',
37608     /**
37609      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37610      */
37611     resizable: false,
37612     /**
37613      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37614      */
37615     handleHeight : 8,
37616     /**
37617      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37618      * traditional select (defaults to true)
37619      */
37620     editable: true,
37621     /**
37622      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37623      */
37624     allQuery: '',
37625     /**
37626      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37627      */
37628     mode: 'remote',
37629     /**
37630      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37631      * listWidth has a higher value)
37632      */
37633     minListWidth : 70,
37634     /**
37635      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37636      * allow the user to set arbitrary text into the field (defaults to false)
37637      */
37638     forceSelection:false,
37639     /**
37640      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37641      * if typeAhead = true (defaults to 250)
37642      */
37643     typeAheadDelay : 250,
37644     /**
37645      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37646      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37647      */
37648     valueNotFoundText : undefined,
37649     /**
37650      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37651      */
37652     blockFocus : false,
37653     
37654     /**
37655      * @cfg {Boolean} disableClear Disable showing of clear button.
37656      */
37657     disableClear : false,
37658     /**
37659      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37660      */
37661     alwaysQuery : false,
37662     
37663     //private
37664     addicon : false,
37665     editicon: false,
37666     
37667     // element that contains real text value.. (when hidden is used..)
37668      
37669     // private
37670     onRender : function(ct, position){
37671         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37672         if(this.hiddenName){
37673             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37674                     'before', true);
37675             this.hiddenField.value =
37676                 this.hiddenValue !== undefined ? this.hiddenValue :
37677                 this.value !== undefined ? this.value : '';
37678
37679             // prevent input submission
37680             this.el.dom.removeAttribute('name');
37681              
37682              
37683         }
37684         if(Roo.isGecko){
37685             this.el.dom.setAttribute('autocomplete', 'off');
37686         }
37687
37688         var cls = 'x-combo-list';
37689
37690         this.list = new Roo.Layer({
37691             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37692         });
37693
37694         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37695         this.list.setWidth(lw);
37696         this.list.swallowEvent('mousewheel');
37697         this.assetHeight = 0;
37698
37699         if(this.title){
37700             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37701             this.assetHeight += this.header.getHeight();
37702         }
37703
37704         this.innerList = this.list.createChild({cls:cls+'-inner'});
37705         this.innerList.on('mouseover', this.onViewOver, this);
37706         this.innerList.on('mousemove', this.onViewMove, this);
37707         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37708         
37709         if(this.allowBlank && !this.pageSize && !this.disableClear){
37710             this.footer = this.list.createChild({cls:cls+'-ft'});
37711             this.pageTb = new Roo.Toolbar(this.footer);
37712            
37713         }
37714         if(this.pageSize){
37715             this.footer = this.list.createChild({cls:cls+'-ft'});
37716             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37717                     {pageSize: this.pageSize});
37718             
37719         }
37720         
37721         if (this.pageTb && this.allowBlank && !this.disableClear) {
37722             var _this = this;
37723             this.pageTb.add(new Roo.Toolbar.Fill(), {
37724                 cls: 'x-btn-icon x-btn-clear',
37725                 text: '&#160;',
37726                 handler: function()
37727                 {
37728                     _this.collapse();
37729                     _this.clearValue();
37730                     _this.onSelect(false, -1);
37731                 }
37732             });
37733         }
37734         if (this.footer) {
37735             this.assetHeight += this.footer.getHeight();
37736         }
37737         
37738
37739         if(!this.tpl){
37740             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37741         }
37742
37743         this.view = new Roo.View(this.innerList, this.tpl, {
37744             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37745         });
37746
37747         this.view.on('click', this.onViewClick, this);
37748
37749         this.store.on('beforeload', this.onBeforeLoad, this);
37750         this.store.on('load', this.onLoad, this);
37751         this.store.on('loadexception', this.onLoadException, this);
37752
37753         if(this.resizable){
37754             this.resizer = new Roo.Resizable(this.list,  {
37755                pinned:true, handles:'se'
37756             });
37757             this.resizer.on('resize', function(r, w, h){
37758                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37759                 this.listWidth = w;
37760                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37761                 this.restrictHeight();
37762             }, this);
37763             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37764         }
37765         if(!this.editable){
37766             this.editable = true;
37767             this.setEditable(false);
37768         }  
37769         
37770         
37771         if (typeof(this.events.add.listeners) != 'undefined') {
37772             
37773             this.addicon = this.wrap.createChild(
37774                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37775        
37776             this.addicon.on('click', function(e) {
37777                 this.fireEvent('add', this);
37778             }, this);
37779         }
37780         if (typeof(this.events.edit.listeners) != 'undefined') {
37781             
37782             this.editicon = this.wrap.createChild(
37783                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37784             if (this.addicon) {
37785                 this.editicon.setStyle('margin-left', '40px');
37786             }
37787             this.editicon.on('click', function(e) {
37788                 
37789                 // we fire even  if inothing is selected..
37790                 this.fireEvent('edit', this, this.lastData );
37791                 
37792             }, this);
37793         }
37794         
37795         
37796         
37797     },
37798
37799     // private
37800     initEvents : function(){
37801         Roo.form.ComboBox.superclass.initEvents.call(this);
37802
37803         this.keyNav = new Roo.KeyNav(this.el, {
37804             "up" : function(e){
37805                 this.inKeyMode = true;
37806                 this.selectPrev();
37807             },
37808
37809             "down" : function(e){
37810                 if(!this.isExpanded()){
37811                     this.onTriggerClick();
37812                 }else{
37813                     this.inKeyMode = true;
37814                     this.selectNext();
37815                 }
37816             },
37817
37818             "enter" : function(e){
37819                 this.onViewClick();
37820                 //return true;
37821             },
37822
37823             "esc" : function(e){
37824                 this.collapse();
37825             },
37826
37827             "tab" : function(e){
37828                 this.onViewClick(false);
37829                 this.fireEvent("specialkey", this, e);
37830                 return true;
37831             },
37832
37833             scope : this,
37834
37835             doRelay : function(foo, bar, hname){
37836                 if(hname == 'down' || this.scope.isExpanded()){
37837                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37838                 }
37839                 return true;
37840             },
37841
37842             forceKeyDown: true
37843         });
37844         this.queryDelay = Math.max(this.queryDelay || 10,
37845                 this.mode == 'local' ? 10 : 250);
37846         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37847         if(this.typeAhead){
37848             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37849         }
37850         if(this.editable !== false){
37851             this.el.on("keyup", this.onKeyUp, this);
37852         }
37853         if(this.forceSelection){
37854             this.on('blur', this.doForce, this);
37855         }
37856     },
37857
37858     onDestroy : function(){
37859         if(this.view){
37860             this.view.setStore(null);
37861             this.view.el.removeAllListeners();
37862             this.view.el.remove();
37863             this.view.purgeListeners();
37864         }
37865         if(this.list){
37866             this.list.destroy();
37867         }
37868         if(this.store){
37869             this.store.un('beforeload', this.onBeforeLoad, this);
37870             this.store.un('load', this.onLoad, this);
37871             this.store.un('loadexception', this.onLoadException, this);
37872         }
37873         Roo.form.ComboBox.superclass.onDestroy.call(this);
37874     },
37875
37876     // private
37877     fireKey : function(e){
37878         if(e.isNavKeyPress() && !this.list.isVisible()){
37879             this.fireEvent("specialkey", this, e);
37880         }
37881     },
37882
37883     // private
37884     onResize: function(w, h){
37885         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37886         
37887         if(typeof w != 'number'){
37888             // we do not handle it!?!?
37889             return;
37890         }
37891         var tw = this.trigger.getWidth();
37892         tw += this.addicon ? this.addicon.getWidth() : 0;
37893         tw += this.editicon ? this.editicon.getWidth() : 0;
37894         var x = w - tw;
37895         this.el.setWidth( this.adjustWidth('input', x));
37896             
37897         this.trigger.setStyle('left', x+'px');
37898         
37899         if(this.list && this.listWidth === undefined){
37900             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37901             this.list.setWidth(lw);
37902             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37903         }
37904         
37905     
37906         
37907     },
37908
37909     /**
37910      * Allow or prevent the user from directly editing the field text.  If false is passed,
37911      * the user will only be able to select from the items defined in the dropdown list.  This method
37912      * is the runtime equivalent of setting the 'editable' config option at config time.
37913      * @param {Boolean} value True to allow the user to directly edit the field text
37914      */
37915     setEditable : function(value){
37916         if(value == this.editable){
37917             return;
37918         }
37919         this.editable = value;
37920         if(!value){
37921             this.el.dom.setAttribute('readOnly', true);
37922             this.el.on('mousedown', this.onTriggerClick,  this);
37923             this.el.addClass('x-combo-noedit');
37924         }else{
37925             this.el.dom.setAttribute('readOnly', false);
37926             this.el.un('mousedown', this.onTriggerClick,  this);
37927             this.el.removeClass('x-combo-noedit');
37928         }
37929     },
37930
37931     // private
37932     onBeforeLoad : function(){
37933         if(!this.hasFocus){
37934             return;
37935         }
37936         this.innerList.update(this.loadingText ?
37937                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37938         this.restrictHeight();
37939         this.selectedIndex = -1;
37940     },
37941
37942     // private
37943     onLoad : function(){
37944         if(!this.hasFocus){
37945             return;
37946         }
37947         if(this.store.getCount() > 0){
37948             this.expand();
37949             this.restrictHeight();
37950             if(this.lastQuery == this.allQuery){
37951                 if(this.editable){
37952                     this.el.dom.select();
37953                 }
37954                 if(!this.selectByValue(this.value, true)){
37955                     this.select(0, true);
37956                 }
37957             }else{
37958                 this.selectNext();
37959                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37960                     this.taTask.delay(this.typeAheadDelay);
37961                 }
37962             }
37963         }else{
37964             this.onEmptyResults();
37965         }
37966         //this.el.focus();
37967     },
37968     // private
37969     onLoadException : function()
37970     {
37971         this.collapse();
37972         Roo.log(this.store.reader.jsonData);
37973         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37974             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37975         }
37976         
37977         
37978     },
37979     // private
37980     onTypeAhead : function(){
37981         if(this.store.getCount() > 0){
37982             var r = this.store.getAt(0);
37983             var newValue = r.data[this.displayField];
37984             var len = newValue.length;
37985             var selStart = this.getRawValue().length;
37986             if(selStart != len){
37987                 this.setRawValue(newValue);
37988                 this.selectText(selStart, newValue.length);
37989             }
37990         }
37991     },
37992
37993     // private
37994     onSelect : function(record, index){
37995         if(this.fireEvent('beforeselect', this, record, index) !== false){
37996             this.setFromData(index > -1 ? record.data : false);
37997             this.collapse();
37998             this.fireEvent('select', this, record, index);
37999         }
38000     },
38001
38002     /**
38003      * Returns the currently selected field value or empty string if no value is set.
38004      * @return {String} value The selected value
38005      */
38006     getValue : function(){
38007         if(this.valueField){
38008             return typeof this.value != 'undefined' ? this.value : '';
38009         }else{
38010             return Roo.form.ComboBox.superclass.getValue.call(this);
38011         }
38012     },
38013
38014     /**
38015      * Clears any text/value currently set in the field
38016      */
38017     clearValue : function(){
38018         if(this.hiddenField){
38019             this.hiddenField.value = '';
38020         }
38021         this.value = '';
38022         this.setRawValue('');
38023         this.lastSelectionText = '';
38024         this.applyEmptyText();
38025     },
38026
38027     /**
38028      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38029      * will be displayed in the field.  If the value does not match the data value of an existing item,
38030      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38031      * Otherwise the field will be blank (although the value will still be set).
38032      * @param {String} value The value to match
38033      */
38034     setValue : function(v){
38035         var text = v;
38036         if(this.valueField){
38037             var r = this.findRecord(this.valueField, v);
38038             if(r){
38039                 text = r.data[this.displayField];
38040             }else if(this.valueNotFoundText !== undefined){
38041                 text = this.valueNotFoundText;
38042             }
38043         }
38044         this.lastSelectionText = text;
38045         if(this.hiddenField){
38046             this.hiddenField.value = v;
38047         }
38048         Roo.form.ComboBox.superclass.setValue.call(this, text);
38049         this.value = v;
38050     },
38051     /**
38052      * @property {Object} the last set data for the element
38053      */
38054     
38055     lastData : false,
38056     /**
38057      * Sets the value of the field based on a object which is related to the record format for the store.
38058      * @param {Object} value the value to set as. or false on reset?
38059      */
38060     setFromData : function(o){
38061         var dv = ''; // display value
38062         var vv = ''; // value value..
38063         this.lastData = o;
38064         if (this.displayField) {
38065             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
38066         } else {
38067             // this is an error condition!!!
38068             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
38069         }
38070         
38071         if(this.valueField){
38072             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
38073         }
38074         if(this.hiddenField){
38075             this.hiddenField.value = vv;
38076             
38077             this.lastSelectionText = dv;
38078             Roo.form.ComboBox.superclass.setValue.call(this, dv);
38079             this.value = vv;
38080             return;
38081         }
38082         // no hidden field.. - we store the value in 'value', but still display
38083         // display field!!!!
38084         this.lastSelectionText = dv;
38085         Roo.form.ComboBox.superclass.setValue.call(this, dv);
38086         this.value = vv;
38087         
38088         
38089     },
38090     // private
38091     reset : function(){
38092         // overridden so that last data is reset..
38093         this.setValue(this.originalValue);
38094         this.clearInvalid();
38095         this.lastData = false;
38096     },
38097     // private
38098     findRecord : function(prop, value){
38099         var record;
38100         if(this.store.getCount() > 0){
38101             this.store.each(function(r){
38102                 if(r.data[prop] == value){
38103                     record = r;
38104                     return false;
38105                 }
38106                 return true;
38107             });
38108         }
38109         return record;
38110     },
38111     
38112     getName: function()
38113     {
38114         // returns hidden if it's set..
38115         if (!this.rendered) {return ''};
38116         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38117         
38118     },
38119     // private
38120     onViewMove : function(e, t){
38121         this.inKeyMode = false;
38122     },
38123
38124     // private
38125     onViewOver : function(e, t){
38126         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38127             return;
38128         }
38129         var item = this.view.findItemFromChild(t);
38130         if(item){
38131             var index = this.view.indexOf(item);
38132             this.select(index, false);
38133         }
38134     },
38135
38136     // private
38137     onViewClick : function(doFocus)
38138     {
38139         var index = this.view.getSelectedIndexes()[0];
38140         var r = this.store.getAt(index);
38141         if(r){
38142             this.onSelect(r, index);
38143         }
38144         if(doFocus !== false && !this.blockFocus){
38145             this.el.focus();
38146         }
38147     },
38148
38149     // private
38150     restrictHeight : function(){
38151         this.innerList.dom.style.height = '';
38152         var inner = this.innerList.dom;
38153         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38154         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38155         this.list.beginUpdate();
38156         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38157         this.list.alignTo(this.el, this.listAlign);
38158         this.list.endUpdate();
38159     },
38160
38161     // private
38162     onEmptyResults : function(){
38163         this.collapse();
38164     },
38165
38166     /**
38167      * Returns true if the dropdown list is expanded, else false.
38168      */
38169     isExpanded : function(){
38170         return this.list.isVisible();
38171     },
38172
38173     /**
38174      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38175      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38176      * @param {String} value The data value of the item to select
38177      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38178      * selected item if it is not currently in view (defaults to true)
38179      * @return {Boolean} True if the value matched an item in the list, else false
38180      */
38181     selectByValue : function(v, scrollIntoView){
38182         if(v !== undefined && v !== null){
38183             var r = this.findRecord(this.valueField || this.displayField, v);
38184             if(r){
38185                 this.select(this.store.indexOf(r), scrollIntoView);
38186                 return true;
38187             }
38188         }
38189         return false;
38190     },
38191
38192     /**
38193      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38194      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38195      * @param {Number} index The zero-based index of the list item to select
38196      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38197      * selected item if it is not currently in view (defaults to true)
38198      */
38199     select : function(index, scrollIntoView){
38200         this.selectedIndex = index;
38201         this.view.select(index);
38202         if(scrollIntoView !== false){
38203             var el = this.view.getNode(index);
38204             if(el){
38205                 this.innerList.scrollChildIntoView(el, false);
38206             }
38207         }
38208     },
38209
38210     // private
38211     selectNext : function(){
38212         var ct = this.store.getCount();
38213         if(ct > 0){
38214             if(this.selectedIndex == -1){
38215                 this.select(0);
38216             }else if(this.selectedIndex < ct-1){
38217                 this.select(this.selectedIndex+1);
38218             }
38219         }
38220     },
38221
38222     // private
38223     selectPrev : function(){
38224         var ct = this.store.getCount();
38225         if(ct > 0){
38226             if(this.selectedIndex == -1){
38227                 this.select(0);
38228             }else if(this.selectedIndex != 0){
38229                 this.select(this.selectedIndex-1);
38230             }
38231         }
38232     },
38233
38234     // private
38235     onKeyUp : function(e){
38236         if(this.editable !== false && !e.isSpecialKey()){
38237             this.lastKey = e.getKey();
38238             this.dqTask.delay(this.queryDelay);
38239         }
38240     },
38241
38242     // private
38243     validateBlur : function(){
38244         return !this.list || !this.list.isVisible();   
38245     },
38246
38247     // private
38248     initQuery : function(){
38249         this.doQuery(this.getRawValue());
38250     },
38251
38252     // private
38253     doForce : function(){
38254         if(this.el.dom.value.length > 0){
38255             this.el.dom.value =
38256                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38257             this.applyEmptyText();
38258         }
38259     },
38260
38261     /**
38262      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38263      * query allowing the query action to be canceled if needed.
38264      * @param {String} query The SQL query to execute
38265      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38266      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38267      * saved in the current store (defaults to false)
38268      */
38269     doQuery : function(q, forceAll){
38270         if(q === undefined || q === null){
38271             q = '';
38272         }
38273         var qe = {
38274             query: q,
38275             forceAll: forceAll,
38276             combo: this,
38277             cancel:false
38278         };
38279         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38280             return false;
38281         }
38282         q = qe.query;
38283         forceAll = qe.forceAll;
38284         if(forceAll === true || (q.length >= this.minChars)){
38285             if(this.lastQuery != q || this.alwaysQuery){
38286                 this.lastQuery = q;
38287                 if(this.mode == 'local'){
38288                     this.selectedIndex = -1;
38289                     if(forceAll){
38290                         this.store.clearFilter();
38291                     }else{
38292                         this.store.filter(this.displayField, q);
38293                     }
38294                     this.onLoad();
38295                 }else{
38296                     this.store.baseParams[this.queryParam] = q;
38297                     this.store.load({
38298                         params: this.getParams(q)
38299                     });
38300                     this.expand();
38301                 }
38302             }else{
38303                 this.selectedIndex = -1;
38304                 this.onLoad();   
38305             }
38306         }
38307     },
38308
38309     // private
38310     getParams : function(q){
38311         var p = {};
38312         //p[this.queryParam] = q;
38313         if(this.pageSize){
38314             p.start = 0;
38315             p.limit = this.pageSize;
38316         }
38317         return p;
38318     },
38319
38320     /**
38321      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38322      */
38323     collapse : function(){
38324         if(!this.isExpanded()){
38325             return;
38326         }
38327         this.list.hide();
38328         Roo.get(document).un('mousedown', this.collapseIf, this);
38329         Roo.get(document).un('mousewheel', this.collapseIf, this);
38330         if (!this.editable) {
38331             Roo.get(document).un('keydown', this.listKeyPress, this);
38332         }
38333         this.fireEvent('collapse', this);
38334     },
38335
38336     // private
38337     collapseIf : function(e){
38338         if(!e.within(this.wrap) && !e.within(this.list)){
38339             this.collapse();
38340         }
38341     },
38342
38343     /**
38344      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38345      */
38346     expand : function(){
38347         if(this.isExpanded() || !this.hasFocus){
38348             return;
38349         }
38350         this.list.alignTo(this.el, this.listAlign);
38351         this.list.show();
38352         Roo.get(document).on('mousedown', this.collapseIf, this);
38353         Roo.get(document).on('mousewheel', this.collapseIf, this);
38354         if (!this.editable) {
38355             Roo.get(document).on('keydown', this.listKeyPress, this);
38356         }
38357         
38358         this.fireEvent('expand', this);
38359     },
38360
38361     // private
38362     // Implements the default empty TriggerField.onTriggerClick function
38363     onTriggerClick : function(){
38364         if(this.disabled){
38365             return;
38366         }
38367         if(this.isExpanded()){
38368             this.collapse();
38369             if (!this.blockFocus) {
38370                 this.el.focus();
38371             }
38372             
38373         }else {
38374             this.hasFocus = true;
38375             if(this.triggerAction == 'all') {
38376                 this.doQuery(this.allQuery, true);
38377             } else {
38378                 this.doQuery(this.getRawValue());
38379             }
38380             if (!this.blockFocus) {
38381                 this.el.focus();
38382             }
38383         }
38384     },
38385     listKeyPress : function(e)
38386     {
38387         //Roo.log('listkeypress');
38388         // scroll to first matching element based on key pres..
38389         if (e.isSpecialKey()) {
38390             return false;
38391         }
38392         var k = String.fromCharCode(e.getKey()).toUpperCase();
38393         //Roo.log(k);
38394         var match  = false;
38395         var csel = this.view.getSelectedNodes();
38396         var cselitem = false;
38397         if (csel.length) {
38398             var ix = this.view.indexOf(csel[0]);
38399             cselitem  = this.store.getAt(ix);
38400             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38401                 cselitem = false;
38402             }
38403             
38404         }
38405         
38406         this.store.each(function(v) { 
38407             if (cselitem) {
38408                 // start at existing selection.
38409                 if (cselitem.id == v.id) {
38410                     cselitem = false;
38411                 }
38412                 return;
38413             }
38414                 
38415             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38416                 match = this.store.indexOf(v);
38417                 return false;
38418             }
38419         }, this);
38420         
38421         if (match === false) {
38422             return true; // no more action?
38423         }
38424         // scroll to?
38425         this.view.select(match);
38426         var sn = Roo.get(this.view.getSelectedNodes()[0])
38427         sn.scrollIntoView(sn.dom.parentNode, false);
38428     }
38429
38430     /** 
38431     * @cfg {Boolean} grow 
38432     * @hide 
38433     */
38434     /** 
38435     * @cfg {Number} growMin 
38436     * @hide 
38437     */
38438     /** 
38439     * @cfg {Number} growMax 
38440     * @hide 
38441     */
38442     /**
38443      * @hide
38444      * @method autoSize
38445      */
38446 });/*
38447  * Based on:
38448  * Ext JS Library 1.1.1
38449  * Copyright(c) 2006-2007, Ext JS, LLC.
38450  *
38451  * Originally Released Under LGPL - original licence link has changed is not relivant.
38452  *
38453  * Fork - LGPL
38454  * <script type="text/javascript">
38455  */
38456 /**
38457  * @class Roo.form.Checkbox
38458  * @extends Roo.form.Field
38459  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38460  * @constructor
38461  * Creates a new Checkbox
38462  * @param {Object} config Configuration options
38463  */
38464 Roo.form.Checkbox = function(config){
38465     Roo.form.Checkbox.superclass.constructor.call(this, config);
38466     this.addEvents({
38467         /**
38468          * @event check
38469          * Fires when the checkbox is checked or unchecked.
38470              * @param {Roo.form.Checkbox} this This checkbox
38471              * @param {Boolean} checked The new checked value
38472              */
38473         check : true
38474     });
38475 };
38476
38477 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38478     /**
38479      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38480      */
38481     focusClass : undefined,
38482     /**
38483      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38484      */
38485     fieldClass: "x-form-field",
38486     /**
38487      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38488      */
38489     checked: false,
38490     /**
38491      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38492      * {tag: "input", type: "checkbox", autocomplete: "off"})
38493      */
38494     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38495     /**
38496      * @cfg {String} boxLabel The text that appears beside the checkbox
38497      */
38498     boxLabel : "",
38499     /**
38500      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38501      */  
38502     inputValue : '1',
38503     /**
38504      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38505      */
38506      valueOff: '0', // value when not checked..
38507
38508     actionMode : 'viewEl', 
38509     //
38510     // private
38511     itemCls : 'x-menu-check-item x-form-item',
38512     groupClass : 'x-menu-group-item',
38513     inputType : 'hidden',
38514     
38515     
38516     inSetChecked: false, // check that we are not calling self...
38517     
38518     inputElement: false, // real input element?
38519     basedOn: false, // ????
38520     
38521     isFormField: true, // not sure where this is needed!!!!
38522
38523     onResize : function(){
38524         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38525         if(!this.boxLabel){
38526             this.el.alignTo(this.wrap, 'c-c');
38527         }
38528     },
38529
38530     initEvents : function(){
38531         Roo.form.Checkbox.superclass.initEvents.call(this);
38532         this.el.on("click", this.onClick,  this);
38533         this.el.on("change", this.onClick,  this);
38534     },
38535
38536
38537     getResizeEl : function(){
38538         return this.wrap;
38539     },
38540
38541     getPositionEl : function(){
38542         return this.wrap;
38543     },
38544
38545     // private
38546     onRender : function(ct, position){
38547         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38548         /*
38549         if(this.inputValue !== undefined){
38550             this.el.dom.value = this.inputValue;
38551         }
38552         */
38553         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38554         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38555         var viewEl = this.wrap.createChild({ 
38556             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38557         this.viewEl = viewEl;   
38558         this.wrap.on('click', this.onClick,  this); 
38559         
38560         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38561         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38562         
38563         
38564         
38565         if(this.boxLabel){
38566             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38567         //    viewEl.on('click', this.onClick,  this); 
38568         }
38569         //if(this.checked){
38570             this.setChecked(this.checked);
38571         //}else{
38572             //this.checked = this.el.dom;
38573         //}
38574
38575     },
38576
38577     // private
38578     initValue : Roo.emptyFn,
38579
38580     /**
38581      * Returns the checked state of the checkbox.
38582      * @return {Boolean} True if checked, else false
38583      */
38584     getValue : function(){
38585         if(this.el){
38586             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38587         }
38588         return this.valueOff;
38589         
38590     },
38591
38592         // private
38593     onClick : function(){ 
38594         this.setChecked(!this.checked);
38595
38596         //if(this.el.dom.checked != this.checked){
38597         //    this.setValue(this.el.dom.checked);
38598        // }
38599     },
38600
38601     /**
38602      * Sets the checked state of the checkbox.
38603      * On is always based on a string comparison between inputValue and the param.
38604      * @param {Boolean/String} value - the value to set 
38605      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38606      */
38607     setValue : function(v,suppressEvent){
38608         
38609         
38610         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38611         //if(this.el && this.el.dom){
38612         //    this.el.dom.checked = this.checked;
38613         //    this.el.dom.defaultChecked = this.checked;
38614         //}
38615         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38616         //this.fireEvent("check", this, this.checked);
38617     },
38618     // private..
38619     setChecked : function(state,suppressEvent)
38620     {
38621         if (this.inSetChecked) {
38622             this.checked = state;
38623             return;
38624         }
38625         
38626     
38627         if(this.wrap){
38628             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38629         }
38630         this.checked = state;
38631         if(suppressEvent !== true){
38632             this.fireEvent('check', this, state);
38633         }
38634         this.inSetChecked = true;
38635         this.el.dom.value = state ? this.inputValue : this.valueOff;
38636         this.inSetChecked = false;
38637         
38638     },
38639     // handle setting of hidden value by some other method!!?!?
38640     setFromHidden: function()
38641     {
38642         if(!this.el){
38643             return;
38644         }
38645         //console.log("SET FROM HIDDEN");
38646         //alert('setFrom hidden');
38647         this.setValue(this.el.dom.value);
38648     },
38649     
38650     onDestroy : function()
38651     {
38652         if(this.viewEl){
38653             Roo.get(this.viewEl).remove();
38654         }
38655          
38656         Roo.form.Checkbox.superclass.onDestroy.call(this);
38657     }
38658
38659 });/*
38660  * Based on:
38661  * Ext JS Library 1.1.1
38662  * Copyright(c) 2006-2007, Ext JS, LLC.
38663  *
38664  * Originally Released Under LGPL - original licence link has changed is not relivant.
38665  *
38666  * Fork - LGPL
38667  * <script type="text/javascript">
38668  */
38669  
38670 /**
38671  * @class Roo.form.Radio
38672  * @extends Roo.form.Checkbox
38673  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38674  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38675  * @constructor
38676  * Creates a new Radio
38677  * @param {Object} config Configuration options
38678  */
38679 Roo.form.Radio = function(){
38680     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38681 };
38682 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38683     inputType: 'radio',
38684
38685     /**
38686      * If this radio is part of a group, it will return the selected value
38687      * @return {String}
38688      */
38689     getGroupValue : function(){
38690         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38691     }
38692 });//<script type="text/javascript">
38693
38694 /*
38695  * Ext JS Library 1.1.1
38696  * Copyright(c) 2006-2007, Ext JS, LLC.
38697  * licensing@extjs.com
38698  * 
38699  * http://www.extjs.com/license
38700  */
38701  
38702  /*
38703   * 
38704   * Known bugs:
38705   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38706   * - IE ? - no idea how much works there.
38707   * 
38708   * 
38709   * 
38710   */
38711  
38712
38713 /**
38714  * @class Ext.form.HtmlEditor
38715  * @extends Ext.form.Field
38716  * Provides a lightweight HTML Editor component.
38717  *
38718  * This has been tested on Fireforx / Chrome.. IE may not be so great..
38719  * 
38720  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38721  * supported by this editor.</b><br/><br/>
38722  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38723  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38724  */
38725 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38726       /**
38727      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38728      */
38729     toolbars : false,
38730     /**
38731      * @cfg {String} createLinkText The default text for the create link prompt
38732      */
38733     createLinkText : 'Please enter the URL for the link:',
38734     /**
38735      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38736      */
38737     defaultLinkValue : 'http:/'+'/',
38738    
38739      /**
38740      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38741      *                        Roo.resizable.
38742      */
38743     resizable : false,
38744      /**
38745      * @cfg {Number} height (in pixels)
38746      */   
38747     height: 300,
38748    /**
38749      * @cfg {Number} width (in pixels)
38750      */   
38751     width: 500,
38752     
38753     /**
38754      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38755      * 
38756      */
38757     stylesheets: false,
38758     
38759     // id of frame..
38760     frameId: false,
38761     
38762     // private properties
38763     validationEvent : false,
38764     deferHeight: true,
38765     initialized : false,
38766     activated : false,
38767     sourceEditMode : false,
38768     onFocus : Roo.emptyFn,
38769     iframePad:3,
38770     hideMode:'offsets',
38771     
38772     defaultAutoCreate : { // modified by initCompnoent..
38773         tag: "textarea",
38774         style:"width:500px;height:300px;",
38775         autocomplete: "off"
38776     },
38777
38778     // private
38779     initComponent : function(){
38780         this.addEvents({
38781             /**
38782              * @event initialize
38783              * Fires when the editor is fully initialized (including the iframe)
38784              * @param {HtmlEditor} this
38785              */
38786             initialize: true,
38787             /**
38788              * @event activate
38789              * Fires when the editor is first receives the focus. Any insertion must wait
38790              * until after this event.
38791              * @param {HtmlEditor} this
38792              */
38793             activate: true,
38794              /**
38795              * @event beforesync
38796              * Fires before the textarea is updated with content from the editor iframe. Return false
38797              * to cancel the sync.
38798              * @param {HtmlEditor} this
38799              * @param {String} html
38800              */
38801             beforesync: true,
38802              /**
38803              * @event beforepush
38804              * Fires before the iframe editor is updated with content from the textarea. Return false
38805              * to cancel the push.
38806              * @param {HtmlEditor} this
38807              * @param {String} html
38808              */
38809             beforepush: true,
38810              /**
38811              * @event sync
38812              * Fires when the textarea is updated with content from the editor iframe.
38813              * @param {HtmlEditor} this
38814              * @param {String} html
38815              */
38816             sync: true,
38817              /**
38818              * @event push
38819              * Fires when the iframe editor is updated with content from the textarea.
38820              * @param {HtmlEditor} this
38821              * @param {String} html
38822              */
38823             push: true,
38824              /**
38825              * @event editmodechange
38826              * Fires when the editor switches edit modes
38827              * @param {HtmlEditor} this
38828              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38829              */
38830             editmodechange: true,
38831             /**
38832              * @event editorevent
38833              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38834              * @param {HtmlEditor} this
38835              */
38836             editorevent: true
38837         });
38838         this.defaultAutoCreate =  {
38839             tag: "textarea",
38840             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38841             autocomplete: "off"
38842         };
38843     },
38844
38845     /**
38846      * Protected method that will not generally be called directly. It
38847      * is called when the editor creates its toolbar. Override this method if you need to
38848      * add custom toolbar buttons.
38849      * @param {HtmlEditor} editor
38850      */
38851     createToolbar : function(editor){
38852         if (!editor.toolbars || !editor.toolbars.length) {
38853             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38854         }
38855         
38856         for (var i =0 ; i < editor.toolbars.length;i++) {
38857             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38858             editor.toolbars[i].init(editor);
38859         }
38860          
38861         
38862     },
38863
38864     /**
38865      * Protected method that will not generally be called directly. It
38866      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38867      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38868      */
38869     getDocMarkup : function(){
38870         // body styles..
38871         var st = '';
38872         if (this.stylesheets === false) {
38873             
38874             Roo.get(document.head).select('style').each(function(node) {
38875                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38876             });
38877             
38878             Roo.get(document.head).select('link').each(function(node) { 
38879                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38880             });
38881             
38882         } else if (!this.stylesheets.length) {
38883                 // simple..
38884                 st = '<style type="text/css">' +
38885                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38886                    '</style>';
38887         } else {
38888             Roo.each(this.stylesheets, function(s) {
38889                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38890             });
38891             
38892         }
38893         
38894         st +=  '<style type="text/css">' +
38895             'IMG { cursor: pointer } ' +
38896         '</style>';
38897
38898         
38899         return '<html><head>' + st  +
38900             //<style type="text/css">' +
38901             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38902             //'</style>' +
38903             ' </head><body></body></html>';
38904     },
38905
38906     // private
38907     onRender : function(ct, position)
38908     {
38909         var _t = this;
38910         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38911         this.el.dom.style.border = '0 none';
38912         this.el.dom.setAttribute('tabIndex', -1);
38913         this.el.addClass('x-hidden');
38914         if(Roo.isIE){ // fix IE 1px bogus margin
38915             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38916         }
38917         this.wrap = this.el.wrap({
38918             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38919         });
38920         
38921         if (this.resizable) {
38922             this.resizeEl = new Roo.Resizable(this.wrap, {
38923                 pinned : true,
38924                 wrap: true,
38925                 dynamic : true,
38926                 minHeight : this.height,
38927                 height: this.height,
38928                 handles : this.resizable,
38929                 width: this.width,
38930                 listeners : {
38931                     resize : function(r, w, h) {
38932                         _t.onResize(w,h); // -something
38933                     }
38934                 }
38935             });
38936             
38937         }
38938
38939         this.frameId = Roo.id();
38940         
38941         this.createToolbar(this);
38942         
38943       
38944         
38945         var iframe = this.wrap.createChild({
38946             tag: 'iframe',
38947             id: this.frameId,
38948             name: this.frameId,
38949             frameBorder : 'no',
38950             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38951         }, this.el
38952         );
38953         
38954        // console.log(iframe);
38955         //this.wrap.dom.appendChild(iframe);
38956
38957         this.iframe = iframe.dom;
38958
38959          this.assignDocWin();
38960         
38961         this.doc.designMode = 'on';
38962        
38963         this.doc.open();
38964         this.doc.write(this.getDocMarkup());
38965         this.doc.close();
38966
38967         
38968         var task = { // must defer to wait for browser to be ready
38969             run : function(){
38970                 //console.log("run task?" + this.doc.readyState);
38971                 this.assignDocWin();
38972                 if(this.doc.body || this.doc.readyState == 'complete'){
38973                     try {
38974                         this.doc.designMode="on";
38975                     } catch (e) {
38976                         return;
38977                     }
38978                     Roo.TaskMgr.stop(task);
38979                     this.initEditor.defer(10, this);
38980                 }
38981             },
38982             interval : 10,
38983             duration:10000,
38984             scope: this
38985         };
38986         Roo.TaskMgr.start(task);
38987
38988         if(!this.width){
38989             this.setSize(this.wrap.getSize());
38990         }
38991         if (this.resizeEl) {
38992             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38993             // should trigger onReize..
38994         }
38995     },
38996
38997     // private
38998     onResize : function(w, h)
38999     {
39000         //Roo.log('resize: ' +w + ',' + h );
39001         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
39002         if(this.el && this.iframe){
39003             if(typeof w == 'number'){
39004                 var aw = w - this.wrap.getFrameWidth('lr');
39005                 this.el.setWidth(this.adjustWidth('textarea', aw));
39006                 this.iframe.style.width = aw + 'px';
39007             }
39008             if(typeof h == 'number'){
39009                 var tbh = 0;
39010                 for (var i =0; i < this.toolbars.length;i++) {
39011                     // fixme - ask toolbars for heights?
39012                     tbh += this.toolbars[i].tb.el.getHeight();
39013                     if (this.toolbars[i].footer) {
39014                         tbh += this.toolbars[i].footer.el.getHeight();
39015                     }
39016                 }
39017                 
39018                 
39019                 
39020                 
39021                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
39022                 ah -= 5; // knock a few pixes off for look..
39023                 this.el.setHeight(this.adjustWidth('textarea', ah));
39024                 this.iframe.style.height = ah + 'px';
39025                 if(this.doc){
39026                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
39027                 }
39028             }
39029         }
39030     },
39031
39032     /**
39033      * Toggles the editor between standard and source edit mode.
39034      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
39035      */
39036     toggleSourceEdit : function(sourceEditMode){
39037         
39038         this.sourceEditMode = sourceEditMode === true;
39039         
39040         if(this.sourceEditMode){
39041           
39042             this.syncValue();
39043             this.iframe.className = 'x-hidden';
39044             this.el.removeClass('x-hidden');
39045             this.el.dom.removeAttribute('tabIndex');
39046             this.el.focus();
39047         }else{
39048              
39049             this.pushValue();
39050             this.iframe.className = '';
39051             this.el.addClass('x-hidden');
39052             this.el.dom.setAttribute('tabIndex', -1);
39053             this.deferFocus();
39054         }
39055         this.setSize(this.wrap.getSize());
39056         this.fireEvent('editmodechange', this, this.sourceEditMode);
39057     },
39058
39059     // private used internally
39060     createLink : function(){
39061         var url = prompt(this.createLinkText, this.defaultLinkValue);
39062         if(url && url != 'http:/'+'/'){
39063             this.relayCmd('createlink', url);
39064         }
39065     },
39066
39067     // private (for BoxComponent)
39068     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39069
39070     // private (for BoxComponent)
39071     getResizeEl : function(){
39072         return this.wrap;
39073     },
39074
39075     // private (for BoxComponent)
39076     getPositionEl : function(){
39077         return this.wrap;
39078     },
39079
39080     // private
39081     initEvents : function(){
39082         this.originalValue = this.getValue();
39083     },
39084
39085     /**
39086      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39087      * @method
39088      */
39089     markInvalid : Roo.emptyFn,
39090     /**
39091      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39092      * @method
39093      */
39094     clearInvalid : Roo.emptyFn,
39095
39096     setValue : function(v){
39097         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
39098         this.pushValue();
39099     },
39100
39101     /**
39102      * Protected method that will not generally be called directly. If you need/want
39103      * custom HTML cleanup, this is the method you should override.
39104      * @param {String} html The HTML to be cleaned
39105      * return {String} The cleaned HTML
39106      */
39107     cleanHtml : function(html){
39108         html = String(html);
39109         if(html.length > 5){
39110             if(Roo.isSafari){ // strip safari nonsense
39111                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39112             }
39113         }
39114         if(html == '&nbsp;'){
39115             html = '';
39116         }
39117         return html;
39118     },
39119
39120     /**
39121      * Protected method that will not generally be called directly. Syncs the contents
39122      * of the editor iframe with the textarea.
39123      */
39124     syncValue : function(){
39125         if(this.initialized){
39126             var bd = (this.doc.body || this.doc.documentElement);
39127             //this.cleanUpPaste(); -- this is done else where and causes havoc..
39128             var html = bd.innerHTML;
39129             if(Roo.isSafari){
39130                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39131                 var m = bs.match(/text-align:(.*?);/i);
39132                 if(m && m[1]){
39133                     html = '<div style="'+m[0]+'">' + html + '</div>';
39134                 }
39135             }
39136             html = this.cleanHtml(html);
39137             // fix up the special chars..
39138             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
39139                 return "&#"+b.charCodeAt()+";" 
39140             });
39141             if(this.fireEvent('beforesync', this, html) !== false){
39142                 this.el.dom.value = html;
39143                 this.fireEvent('sync', this, html);
39144             }
39145         }
39146     },
39147
39148     /**
39149      * Protected method that will not generally be called directly. Pushes the value of the textarea
39150      * into the iframe editor.
39151      */
39152     pushValue : function(){
39153         if(this.initialized){
39154             var v = this.el.dom.value;
39155             if(v.length < 1){
39156                 v = '&#160;';
39157             }
39158             
39159             if(this.fireEvent('beforepush', this, v) !== false){
39160                 var d = (this.doc.body || this.doc.documentElement);
39161                 d.innerHTML = v;
39162                 this.cleanUpPaste();
39163                 this.el.dom.value = d.innerHTML;
39164                 this.fireEvent('push', this, v);
39165             }
39166         }
39167     },
39168
39169     // private
39170     deferFocus : function(){
39171         this.focus.defer(10, this);
39172     },
39173
39174     // doc'ed in Field
39175     focus : function(){
39176         if(this.win && !this.sourceEditMode){
39177             this.win.focus();
39178         }else{
39179             this.el.focus();
39180         }
39181     },
39182     
39183     assignDocWin: function()
39184     {
39185         var iframe = this.iframe;
39186         
39187          if(Roo.isIE){
39188             this.doc = iframe.contentWindow.document;
39189             this.win = iframe.contentWindow;
39190         } else {
39191             if (!Roo.get(this.frameId)) {
39192                 return;
39193             }
39194             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39195             this.win = Roo.get(this.frameId).dom.contentWindow;
39196         }
39197     },
39198     
39199     // private
39200     initEditor : function(){
39201         //console.log("INIT EDITOR");
39202         this.assignDocWin();
39203         
39204         
39205         
39206         this.doc.designMode="on";
39207         this.doc.open();
39208         this.doc.write(this.getDocMarkup());
39209         this.doc.close();
39210         
39211         var dbody = (this.doc.body || this.doc.documentElement);
39212         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39213         // this copies styles from the containing element into thsi one..
39214         // not sure why we need all of this..
39215         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39216         ss['background-attachment'] = 'fixed'; // w3c
39217         dbody.bgProperties = 'fixed'; // ie
39218         Roo.DomHelper.applyStyles(dbody, ss);
39219         Roo.EventManager.on(this.doc, {
39220             //'mousedown': this.onEditorEvent,
39221             'mouseup': this.onEditorEvent,
39222             'dblclick': this.onEditorEvent,
39223             'click': this.onEditorEvent,
39224             'keyup': this.onEditorEvent,
39225             buffer:100,
39226             scope: this
39227         });
39228         if(Roo.isGecko){
39229             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39230         }
39231         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39232             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39233         }
39234         this.initialized = true;
39235
39236         this.fireEvent('initialize', this);
39237         this.pushValue();
39238     },
39239
39240     // private
39241     onDestroy : function(){
39242         
39243         
39244         
39245         if(this.rendered){
39246             
39247             for (var i =0; i < this.toolbars.length;i++) {
39248                 // fixme - ask toolbars for heights?
39249                 this.toolbars[i].onDestroy();
39250             }
39251             
39252             this.wrap.dom.innerHTML = '';
39253             this.wrap.remove();
39254         }
39255     },
39256
39257     // private
39258     onFirstFocus : function(){
39259         
39260         this.assignDocWin();
39261         
39262         
39263         this.activated = true;
39264         for (var i =0; i < this.toolbars.length;i++) {
39265             this.toolbars[i].onFirstFocus();
39266         }
39267        
39268         if(Roo.isGecko){ // prevent silly gecko errors
39269             this.win.focus();
39270             var s = this.win.getSelection();
39271             if(!s.focusNode || s.focusNode.nodeType != 3){
39272                 var r = s.getRangeAt(0);
39273                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39274                 r.collapse(true);
39275                 this.deferFocus();
39276             }
39277             try{
39278                 this.execCmd('useCSS', true);
39279                 this.execCmd('styleWithCSS', false);
39280             }catch(e){}
39281         }
39282         this.fireEvent('activate', this);
39283     },
39284
39285     // private
39286     adjustFont: function(btn){
39287         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39288         //if(Roo.isSafari){ // safari
39289         //    adjust *= 2;
39290        // }
39291         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39292         if(Roo.isSafari){ // safari
39293             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39294             v =  (v < 10) ? 10 : v;
39295             v =  (v > 48) ? 48 : v;
39296             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39297             
39298         }
39299         
39300         
39301         v = Math.max(1, v+adjust);
39302         
39303         this.execCmd('FontSize', v  );
39304     },
39305
39306     onEditorEvent : function(e){
39307         this.fireEvent('editorevent', this, e);
39308       //  this.updateToolbar();
39309         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39310     },
39311
39312     insertTag : function(tg)
39313     {
39314         // could be a bit smarter... -> wrap the current selected tRoo..
39315         
39316         this.execCmd("formatblock",   tg);
39317         
39318     },
39319     
39320     insertText : function(txt)
39321     {
39322         
39323         
39324         range = this.createRange();
39325         range.deleteContents();
39326                //alert(Sender.getAttribute('label'));
39327                
39328         range.insertNode(this.doc.createTextNode(txt));
39329     } ,
39330     
39331     // private
39332     relayBtnCmd : function(btn){
39333         this.relayCmd(btn.cmd);
39334     },
39335
39336     /**
39337      * Executes a Midas editor command on the editor document and performs necessary focus and
39338      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39339      * @param {String} cmd The Midas command
39340      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39341      */
39342     relayCmd : function(cmd, value){
39343         this.win.focus();
39344         this.execCmd(cmd, value);
39345         this.fireEvent('editorevent', this);
39346         //this.updateToolbar();
39347         this.deferFocus();
39348     },
39349
39350     /**
39351      * Executes a Midas editor command directly on the editor document.
39352      * For visual commands, you should use {@link #relayCmd} instead.
39353      * <b>This should only be called after the editor is initialized.</b>
39354      * @param {String} cmd The Midas command
39355      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39356      */
39357     execCmd : function(cmd, value){
39358         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39359         this.syncValue();
39360     },
39361  
39362  
39363    
39364     /**
39365      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39366      * to insert tRoo.
39367      * @param {String} text | dom node.. 
39368      */
39369     insertAtCursor : function(text)
39370     {
39371         
39372         
39373         
39374         if(!this.activated){
39375             return;
39376         }
39377         /*
39378         if(Roo.isIE){
39379             this.win.focus();
39380             var r = this.doc.selection.createRange();
39381             if(r){
39382                 r.collapse(true);
39383                 r.pasteHTML(text);
39384                 this.syncValue();
39385                 this.deferFocus();
39386             
39387             }
39388             return;
39389         }
39390         */
39391         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39392             this.win.focus();
39393             
39394             
39395             // from jquery ui (MIT licenced)
39396             var range, node;
39397             var win = this.win;
39398             
39399             if (win.getSelection && win.getSelection().getRangeAt) {
39400                 range = win.getSelection().getRangeAt(0);
39401                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39402                 range.insertNode(node);
39403             } else if (win.document.selection && win.document.selection.createRange) {
39404                 // no firefox support
39405                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39406                 win.document.selection.createRange().pasteHTML(txt);
39407             } else {
39408                 // no firefox support
39409                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39410                 this.execCmd('InsertHTML', txt);
39411             } 
39412             
39413             this.syncValue();
39414             
39415             this.deferFocus();
39416         }
39417     },
39418  // private
39419     mozKeyPress : function(e){
39420         if(e.ctrlKey){
39421             var c = e.getCharCode(), cmd;
39422           
39423             if(c > 0){
39424                 c = String.fromCharCode(c).toLowerCase();
39425                 switch(c){
39426                     case 'b':
39427                         cmd = 'bold';
39428                         break;
39429                     case 'i':
39430                         cmd = 'italic';
39431                         break;
39432                     
39433                     case 'u':
39434                         cmd = 'underline';
39435                         break;
39436                     
39437                     case 'v':
39438                         this.cleanUpPaste.defer(100, this);
39439                         return;
39440                         
39441                 }
39442                 if(cmd){
39443                     this.win.focus();
39444                     this.execCmd(cmd);
39445                     this.deferFocus();
39446                     e.preventDefault();
39447                 }
39448                 
39449             }
39450         }
39451     },
39452
39453     // private
39454     fixKeys : function(){ // load time branching for fastest keydown performance
39455         if(Roo.isIE){
39456             return function(e){
39457                 var k = e.getKey(), r;
39458                 if(k == e.TAB){
39459                     e.stopEvent();
39460                     r = this.doc.selection.createRange();
39461                     if(r){
39462                         r.collapse(true);
39463                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39464                         this.deferFocus();
39465                     }
39466                     return;
39467                 }
39468                 
39469                 if(k == e.ENTER){
39470                     r = this.doc.selection.createRange();
39471                     if(r){
39472                         var target = r.parentElement();
39473                         if(!target || target.tagName.toLowerCase() != 'li'){
39474                             e.stopEvent();
39475                             r.pasteHTML('<br />');
39476                             r.collapse(false);
39477                             r.select();
39478                         }
39479                     }
39480                 }
39481                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39482                     this.cleanUpPaste.defer(100, this);
39483                     return;
39484                 }
39485                 
39486                 
39487             };
39488         }else if(Roo.isOpera){
39489             return function(e){
39490                 var k = e.getKey();
39491                 if(k == e.TAB){
39492                     e.stopEvent();
39493                     this.win.focus();
39494                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39495                     this.deferFocus();
39496                 }
39497                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39498                     this.cleanUpPaste.defer(100, this);
39499                     return;
39500                 }
39501                 
39502             };
39503         }else if(Roo.isSafari){
39504             return function(e){
39505                 var k = e.getKey();
39506                 
39507                 if(k == e.TAB){
39508                     e.stopEvent();
39509                     this.execCmd('InsertText','\t');
39510                     this.deferFocus();
39511                     return;
39512                 }
39513                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39514                     this.cleanUpPaste.defer(100, this);
39515                     return;
39516                 }
39517                 
39518              };
39519         }
39520     }(),
39521     
39522     getAllAncestors: function()
39523     {
39524         var p = this.getSelectedNode();
39525         var a = [];
39526         if (!p) {
39527             a.push(p); // push blank onto stack..
39528             p = this.getParentElement();
39529         }
39530         
39531         
39532         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39533             a.push(p);
39534             p = p.parentNode;
39535         }
39536         a.push(this.doc.body);
39537         return a;
39538     },
39539     lastSel : false,
39540     lastSelNode : false,
39541     
39542     
39543     getSelection : function() 
39544     {
39545         this.assignDocWin();
39546         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39547     },
39548     
39549     getSelectedNode: function() 
39550     {
39551         // this may only work on Gecko!!!
39552         
39553         // should we cache this!!!!
39554         
39555         
39556         
39557          
39558         var range = this.createRange(this.getSelection()).cloneRange();
39559         
39560         if (Roo.isIE) {
39561             var parent = range.parentElement();
39562             while (true) {
39563                 var testRange = range.duplicate();
39564                 testRange.moveToElementText(parent);
39565                 if (testRange.inRange(range)) {
39566                     break;
39567                 }
39568                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39569                     break;
39570                 }
39571                 parent = parent.parentElement;
39572             }
39573             return parent;
39574         }
39575         
39576         // is ancestor a text element.
39577         var ac =  range.commonAncestorContainer;
39578         if (ac.nodeType == 3) {
39579             ac = ac.parentNode;
39580         }
39581         
39582         var ar = ac.childNodes;
39583          
39584         var nodes = [];
39585         var other_nodes = [];
39586         var has_other_nodes = false;
39587         for (var i=0;i<ar.length;i++) {
39588             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39589                 continue;
39590             }
39591             // fullly contained node.
39592             
39593             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39594                 nodes.push(ar[i]);
39595                 continue;
39596             }
39597             
39598             // probably selected..
39599             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39600                 other_nodes.push(ar[i]);
39601                 continue;
39602             }
39603             // outer..
39604             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39605                 continue;
39606             }
39607             
39608             
39609             has_other_nodes = true;
39610         }
39611         if (!nodes.length && other_nodes.length) {
39612             nodes= other_nodes;
39613         }
39614         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39615             return false;
39616         }
39617         
39618         return nodes[0];
39619     },
39620     createRange: function(sel)
39621     {
39622         // this has strange effects when using with 
39623         // top toolbar - not sure if it's a great idea.
39624         //this.editor.contentWindow.focus();
39625         if (typeof sel != "undefined") {
39626             try {
39627                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39628             } catch(e) {
39629                 return this.doc.createRange();
39630             }
39631         } else {
39632             return this.doc.createRange();
39633         }
39634     },
39635     getParentElement: function()
39636     {
39637         
39638         this.assignDocWin();
39639         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39640         
39641         var range = this.createRange(sel);
39642          
39643         try {
39644             var p = range.commonAncestorContainer;
39645             while (p.nodeType == 3) { // text node
39646                 p = p.parentNode;
39647             }
39648             return p;
39649         } catch (e) {
39650             return null;
39651         }
39652     
39653     },
39654     /***
39655      *
39656      * Range intersection.. the hard stuff...
39657      *  '-1' = before
39658      *  '0' = hits..
39659      *  '1' = after.
39660      *         [ -- selected range --- ]
39661      *   [fail]                        [fail]
39662      *
39663      *    basically..
39664      *      if end is before start or  hits it. fail.
39665      *      if start is after end or hits it fail.
39666      *
39667      *   if either hits (but other is outside. - then it's not 
39668      *   
39669      *    
39670      **/
39671     
39672     
39673     // @see http://www.thismuchiknow.co.uk/?p=64.
39674     rangeIntersectsNode : function(range, node)
39675     {
39676         var nodeRange = node.ownerDocument.createRange();
39677         try {
39678             nodeRange.selectNode(node);
39679         } catch (e) {
39680             nodeRange.selectNodeContents(node);
39681         }
39682     
39683         var rangeStartRange = range.cloneRange();
39684         rangeStartRange.collapse(true);
39685     
39686         var rangeEndRange = range.cloneRange();
39687         rangeEndRange.collapse(false);
39688     
39689         var nodeStartRange = nodeRange.cloneRange();
39690         nodeStartRange.collapse(true);
39691     
39692         var nodeEndRange = nodeRange.cloneRange();
39693         nodeEndRange.collapse(false);
39694     
39695         return rangeStartRange.compareBoundaryPoints(
39696                  Range.START_TO_START, nodeEndRange) == -1 &&
39697                rangeEndRange.compareBoundaryPoints(
39698                  Range.START_TO_START, nodeStartRange) == 1;
39699         
39700          
39701     },
39702     rangeCompareNode : function(range, node)
39703     {
39704         var nodeRange = node.ownerDocument.createRange();
39705         try {
39706             nodeRange.selectNode(node);
39707         } catch (e) {
39708             nodeRange.selectNodeContents(node);
39709         }
39710         
39711         
39712         range.collapse(true);
39713     
39714         nodeRange.collapse(true);
39715      
39716         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39717         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39718          
39719         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39720         
39721         var nodeIsBefore   =  ss == 1;
39722         var nodeIsAfter    = ee == -1;
39723         
39724         if (nodeIsBefore && nodeIsAfter)
39725             return 0; // outer
39726         if (!nodeIsBefore && nodeIsAfter)
39727             return 1; //right trailed.
39728         
39729         if (nodeIsBefore && !nodeIsAfter)
39730             return 2;  // left trailed.
39731         // fully contined.
39732         return 3;
39733     },
39734
39735     // private? - in a new class?
39736     cleanUpPaste :  function()
39737     {
39738         // cleans up the whole document..
39739          Roo.log('cleanuppaste');
39740         this.cleanUpChildren(this.doc.body);
39741         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39742         if (clean != this.doc.body.innerHTML) {
39743             this.doc.body.innerHTML = clean;
39744         }
39745         
39746     },
39747     
39748     cleanWordChars : function(input) {
39749         var he = Roo.form.HtmlEditor;
39750     
39751         var output = input;
39752         Roo.each(he.swapCodes, function(sw) { 
39753         
39754             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39755             output = output.replace(swapper, sw[1]);
39756         });
39757         return output;
39758     },
39759     
39760     
39761     cleanUpChildren : function (n)
39762     {
39763         if (!n.childNodes.length) {
39764             return;
39765         }
39766         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39767            this.cleanUpChild(n.childNodes[i]);
39768         }
39769     },
39770     
39771     
39772         
39773     
39774     cleanUpChild : function (node)
39775     {
39776         //console.log(node);
39777         if (node.nodeName == "#text") {
39778             // clean up silly Windows -- stuff?
39779             return; 
39780         }
39781         if (node.nodeName == "#comment") {
39782             node.parentNode.removeChild(node);
39783             // clean up silly Windows -- stuff?
39784             return; 
39785         }
39786         
39787         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39788             // remove node.
39789             node.parentNode.removeChild(node);
39790             return;
39791             
39792         }
39793         
39794         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39795         
39796         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39797         
39798         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39799             remove_keep_children = true;
39800         }
39801         
39802         if (remove_keep_children) {
39803             this.cleanUpChildren(node);
39804             // inserts everything just before this node...
39805             while (node.childNodes.length) {
39806                 var cn = node.childNodes[0];
39807                 node.removeChild(cn);
39808                 node.parentNode.insertBefore(cn, node);
39809             }
39810             node.parentNode.removeChild(node);
39811             return;
39812         }
39813         
39814         if (!node.attributes || !node.attributes.length) {
39815             this.cleanUpChildren(node);
39816             return;
39817         }
39818         
39819         function cleanAttr(n,v)
39820         {
39821             
39822             if (v.match(/^\./) || v.match(/^\//)) {
39823                 return;
39824             }
39825             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39826                 return;
39827             }
39828             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39829             node.removeAttribute(n);
39830             
39831         }
39832         
39833         function cleanStyle(n,v)
39834         {
39835             if (v.match(/expression/)) { //XSS?? should we even bother..
39836                 node.removeAttribute(n);
39837                 return;
39838             }
39839             
39840             
39841             var parts = v.split(/;/);
39842             Roo.each(parts, function(p) {
39843                 p = p.replace(/\s+/g,'');
39844                 if (!p.length) {
39845                     return true;
39846                 }
39847                 var l = p.split(':').shift().replace(/\s+/g,'');
39848                 
39849                 // only allow 'c whitelisted system attributes'
39850                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39851                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39852                     node.removeAttribute(n);
39853                     return false;
39854                 }
39855                 return true;
39856             });
39857             
39858             
39859         }
39860         
39861         
39862         for (var i = node.attributes.length-1; i > -1 ; i--) {
39863             var a = node.attributes[i];
39864             //console.log(a);
39865             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39866                 node.removeAttribute(a.name);
39867                 return;
39868             }
39869             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39870                 cleanAttr(a.name,a.value); // fixme..
39871                 return;
39872             }
39873             if (a.name == 'style') {
39874                 cleanStyle(a.name,a.value);
39875             }
39876             /// clean up MS crap..
39877             // tecnically this should be a list of valid class'es..
39878             
39879             
39880             if (a.name == 'class') {
39881                 if (a.value.match(/^Mso/)) {
39882                     node.className = '';
39883                 }
39884                 
39885                 if (a.value.match(/body/)) {
39886                     node.className = '';
39887                 }
39888             }
39889             
39890             // style cleanup!?
39891             // class cleanup?
39892             
39893         }
39894         
39895         
39896         this.cleanUpChildren(node);
39897         
39898         
39899     }
39900     
39901     
39902     // hide stuff that is not compatible
39903     /**
39904      * @event blur
39905      * @hide
39906      */
39907     /**
39908      * @event change
39909      * @hide
39910      */
39911     /**
39912      * @event focus
39913      * @hide
39914      */
39915     /**
39916      * @event specialkey
39917      * @hide
39918      */
39919     /**
39920      * @cfg {String} fieldClass @hide
39921      */
39922     /**
39923      * @cfg {String} focusClass @hide
39924      */
39925     /**
39926      * @cfg {String} autoCreate @hide
39927      */
39928     /**
39929      * @cfg {String} inputType @hide
39930      */
39931     /**
39932      * @cfg {String} invalidClass @hide
39933      */
39934     /**
39935      * @cfg {String} invalidText @hide
39936      */
39937     /**
39938      * @cfg {String} msgFx @hide
39939      */
39940     /**
39941      * @cfg {String} validateOnBlur @hide
39942      */
39943 });
39944
39945 Roo.form.HtmlEditor.white = [
39946         'area', 'br', 'img', 'input', 'hr', 'wbr',
39947         
39948        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39949        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39950        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39951        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39952        'table',   'ul',         'xmp', 
39953        
39954        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39955       'thead',   'tr', 
39956      
39957       'dir', 'menu', 'ol', 'ul', 'dl',
39958        
39959       'embed',  'object'
39960 ];
39961
39962
39963 Roo.form.HtmlEditor.black = [
39964     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39965         'applet', // 
39966         'base',   'basefont', 'bgsound', 'blink',  'body', 
39967         'frame',  'frameset', 'head',    'html',   'ilayer', 
39968         'iframe', 'layer',  'link',     'meta',    'object',   
39969         'script', 'style' ,'title',  'xml' // clean later..
39970 ];
39971 Roo.form.HtmlEditor.clean = [
39972     'script', 'style', 'title', 'xml'
39973 ];
39974 Roo.form.HtmlEditor.remove = [
39975     'font'
39976 ];
39977 // attributes..
39978
39979 Roo.form.HtmlEditor.ablack = [
39980     'on'
39981 ];
39982     
39983 Roo.form.HtmlEditor.aclean = [ 
39984     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39985 ];
39986
39987 // protocols..
39988 Roo.form.HtmlEditor.pwhite= [
39989         'http',  'https',  'mailto'
39990 ];
39991
39992 // white listed style attributes.
39993 Roo.form.HtmlEditor.cwhite= [
39994         'text-align',
39995         'font-size'
39996 ];
39997
39998
39999 Roo.form.HtmlEditor.swapCodes   =[ 
40000     [    8211, "--" ], 
40001     [    8212, "--" ], 
40002     [    8216,  "'" ],  
40003     [    8217, "'" ],  
40004     [    8220, '"' ],  
40005     [    8221, '"' ],  
40006     [    8226, "*" ],  
40007     [    8230, "..." ]
40008 ]; 
40009
40010     // <script type="text/javascript">
40011 /*
40012  * Based on
40013  * Ext JS Library 1.1.1
40014  * Copyright(c) 2006-2007, Ext JS, LLC.
40015  *  
40016  
40017  */
40018
40019 /**
40020  * @class Roo.form.HtmlEditorToolbar1
40021  * Basic Toolbar
40022  * 
40023  * Usage:
40024  *
40025  new Roo.form.HtmlEditor({
40026     ....
40027     toolbars : [
40028         new Roo.form.HtmlEditorToolbar1({
40029             disable : { fonts: 1 , format: 1, ..., ... , ...],
40030             btns : [ .... ]
40031         })
40032     }
40033      
40034  * 
40035  * @cfg {Object} disable List of elements to disable..
40036  * @cfg {Array} btns List of additional buttons.
40037  * 
40038  * 
40039  * NEEDS Extra CSS? 
40040  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
40041  */
40042  
40043 Roo.form.HtmlEditor.ToolbarStandard = function(config)
40044 {
40045     
40046     Roo.apply(this, config);
40047     
40048     // default disabled, based on 'good practice'..
40049     this.disable = this.disable || {};
40050     Roo.applyIf(this.disable, {
40051         fontSize : true,
40052         colors : true,
40053         specialElements : true
40054     });
40055     
40056     
40057     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40058     // dont call parent... till later.
40059 }
40060
40061 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
40062     
40063     tb: false,
40064     
40065     rendered: false,
40066     
40067     editor : false,
40068     /**
40069      * @cfg {Object} disable  List of toolbar elements to disable
40070          
40071      */
40072     disable : false,
40073       /**
40074      * @cfg {Array} fontFamilies An array of available font families
40075      */
40076     fontFamilies : [
40077         'Arial',
40078         'Courier New',
40079         'Tahoma',
40080         'Times New Roman',
40081         'Verdana'
40082     ],
40083     
40084     specialChars : [
40085            "&#169;",
40086           "&#174;",     
40087           "&#8482;",    
40088           "&#163;" ,    
40089          // "&#8212;",    
40090           "&#8230;",    
40091           "&#247;" ,    
40092         //  "&#225;" ,     ?? a acute?
40093            "&#8364;"    , //Euro
40094        //   "&#8220;"    ,
40095         //  "&#8221;"    ,
40096         //  "&#8226;"    ,
40097           "&#176;"  //   , // degrees
40098
40099          // "&#233;"     , // e ecute
40100          // "&#250;"     , // u ecute?
40101     ],
40102     
40103     specialElements : [
40104         {
40105             text: "Insert Table",
40106             xtype: 'MenuItem',
40107             xns : Roo.Menu,
40108             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
40109                 
40110         },
40111         {    
40112             text: "Insert Image",
40113             xtype: 'MenuItem',
40114             xns : Roo.Menu,
40115             ihtml : '<img src="about:blank"/>'
40116             
40117         }
40118         
40119          
40120     ],
40121     
40122     
40123     inputElements : [ 
40124             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40125             "input:submit", "input:button", "select", "textarea", "label" ],
40126     formats : [
40127         ["p"] ,  
40128         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40129         ["pre"],[ "code"], 
40130         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40131     ],
40132      /**
40133      * @cfg {String} defaultFont default font to use.
40134      */
40135     defaultFont: 'tahoma',
40136    
40137     fontSelect : false,
40138     
40139     
40140     formatCombo : false,
40141     
40142     init : function(editor)
40143     {
40144         this.editor = editor;
40145         
40146         
40147         var fid = editor.frameId;
40148         var etb = this;
40149         function btn(id, toggle, handler){
40150             var xid = fid + '-'+ id ;
40151             return {
40152                 id : xid,
40153                 cmd : id,
40154                 cls : 'x-btn-icon x-edit-'+id,
40155                 enableToggle:toggle !== false,
40156                 scope: editor, // was editor...
40157                 handler:handler||editor.relayBtnCmd,
40158                 clickEvent:'mousedown',
40159                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40160                 tabIndex:-1
40161             };
40162         }
40163         
40164         
40165         
40166         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40167         this.tb = tb;
40168          // stop form submits
40169         tb.el.on('click', function(e){
40170             e.preventDefault(); // what does this do?
40171         });
40172
40173         if(!this.disable.font && !Roo.isSafari){
40174             /* why no safari for fonts
40175             editor.fontSelect = tb.el.createChild({
40176                 tag:'select',
40177                 tabIndex: -1,
40178                 cls:'x-font-select',
40179                 html: editor.createFontOptions()
40180             });
40181             editor.fontSelect.on('change', function(){
40182                 var font = editor.fontSelect.dom.value;
40183                 editor.relayCmd('fontname', font);
40184                 editor.deferFocus();
40185             }, editor);
40186             tb.add(
40187                 editor.fontSelect.dom,
40188                 '-'
40189             );
40190             */
40191         };
40192         if(!this.disable.formats){
40193             this.formatCombo = new Roo.form.ComboBox({
40194                 store: new Roo.data.SimpleStore({
40195                     id : 'tag',
40196                     fields: ['tag'],
40197                     data : this.formats // from states.js
40198                 }),
40199                 blockFocus : true,
40200                 //autoCreate : {tag: "div",  size: "20"},
40201                 displayField:'tag',
40202                 typeAhead: false,
40203                 mode: 'local',
40204                 editable : false,
40205                 triggerAction: 'all',
40206                 emptyText:'Add tag',
40207                 selectOnFocus:true,
40208                 width:135,
40209                 listeners : {
40210                     'select': function(c, r, i) {
40211                         editor.insertTag(r.get('tag'));
40212                         editor.focus();
40213                     }
40214                 }
40215
40216             });
40217             tb.addField(this.formatCombo);
40218             
40219         }
40220         
40221         if(!this.disable.format){
40222             tb.add(
40223                 btn('bold'),
40224                 btn('italic'),
40225                 btn('underline')
40226             );
40227         };
40228         if(!this.disable.fontSize){
40229             tb.add(
40230                 '-',
40231                 
40232                 
40233                 btn('increasefontsize', false, editor.adjustFont),
40234                 btn('decreasefontsize', false, editor.adjustFont)
40235             );
40236         };
40237         
40238         
40239         if(!this.disable.colors){
40240             tb.add(
40241                 '-', {
40242                     id:editor.frameId +'-forecolor',
40243                     cls:'x-btn-icon x-edit-forecolor',
40244                     clickEvent:'mousedown',
40245                     tooltip: this.buttonTips['forecolor'] || undefined,
40246                     tabIndex:-1,
40247                     menu : new Roo.menu.ColorMenu({
40248                         allowReselect: true,
40249                         focus: Roo.emptyFn,
40250                         value:'000000',
40251                         plain:true,
40252                         selectHandler: function(cp, color){
40253                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40254                             editor.deferFocus();
40255                         },
40256                         scope: editor,
40257                         clickEvent:'mousedown'
40258                     })
40259                 }, {
40260                     id:editor.frameId +'backcolor',
40261                     cls:'x-btn-icon x-edit-backcolor',
40262                     clickEvent:'mousedown',
40263                     tooltip: this.buttonTips['backcolor'] || undefined,
40264                     tabIndex:-1,
40265                     menu : new Roo.menu.ColorMenu({
40266                         focus: Roo.emptyFn,
40267                         value:'FFFFFF',
40268                         plain:true,
40269                         allowReselect: true,
40270                         selectHandler: function(cp, color){
40271                             if(Roo.isGecko){
40272                                 editor.execCmd('useCSS', false);
40273                                 editor.execCmd('hilitecolor', color);
40274                                 editor.execCmd('useCSS', true);
40275                                 editor.deferFocus();
40276                             }else{
40277                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40278                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40279                                 editor.deferFocus();
40280                             }
40281                         },
40282                         scope:editor,
40283                         clickEvent:'mousedown'
40284                     })
40285                 }
40286             );
40287         };
40288         // now add all the items...
40289         
40290
40291         if(!this.disable.alignments){
40292             tb.add(
40293                 '-',
40294                 btn('justifyleft'),
40295                 btn('justifycenter'),
40296                 btn('justifyright')
40297             );
40298         };
40299
40300         //if(!Roo.isSafari){
40301             if(!this.disable.links){
40302                 tb.add(
40303                     '-',
40304                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40305                 );
40306             };
40307
40308             if(!this.disable.lists){
40309                 tb.add(
40310                     '-',
40311                     btn('insertorderedlist'),
40312                     btn('insertunorderedlist')
40313                 );
40314             }
40315             if(!this.disable.sourceEdit){
40316                 tb.add(
40317                     '-',
40318                     btn('sourceedit', true, function(btn){
40319                         this.toggleSourceEdit(btn.pressed);
40320                     })
40321                 );
40322             }
40323         //}
40324         
40325         var smenu = { };
40326         // special menu.. - needs to be tidied up..
40327         if (!this.disable.special) {
40328             smenu = {
40329                 text: "&#169;",
40330                 cls: 'x-edit-none',
40331                 
40332                 menu : {
40333                     items : []
40334                 }
40335             };
40336             for (var i =0; i < this.specialChars.length; i++) {
40337                 smenu.menu.items.push({
40338                     
40339                     html: this.specialChars[i],
40340                     handler: function(a,b) {
40341                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40342                         //editor.insertAtCursor(a.html);
40343                         
40344                     },
40345                     tabIndex:-1
40346                 });
40347             }
40348             
40349             
40350             tb.add(smenu);
40351             
40352             
40353         }
40354          
40355         if (!this.disable.specialElements) {
40356             var semenu = {
40357                 text: "Other;",
40358                 cls: 'x-edit-none',
40359                 menu : {
40360                     items : []
40361                 }
40362             };
40363             for (var i =0; i < this.specialElements.length; i++) {
40364                 semenu.menu.items.push(
40365                     Roo.apply({ 
40366                         handler: function(a,b) {
40367                             editor.insertAtCursor(this.ihtml);
40368                         }
40369                     }, this.specialElements[i])
40370                 );
40371                     
40372             }
40373             
40374             tb.add(semenu);
40375             
40376             
40377         }
40378          
40379         
40380         if (this.btns) {
40381             for(var i =0; i< this.btns.length;i++) {
40382                 var b = this.btns[i];
40383                 b.cls =  'x-edit-none';
40384                 b.scope = editor;
40385                 tb.add(b);
40386             }
40387         
40388         }
40389         
40390         
40391         
40392         // disable everything...
40393         
40394         this.tb.items.each(function(item){
40395            if(item.id != editor.frameId+ '-sourceedit'){
40396                 item.disable();
40397             }
40398         });
40399         this.rendered = true;
40400         
40401         // the all the btns;
40402         editor.on('editorevent', this.updateToolbar, this);
40403         // other toolbars need to implement this..
40404         //editor.on('editmodechange', this.updateToolbar, this);
40405     },
40406     
40407     
40408     
40409     /**
40410      * Protected method that will not generally be called directly. It triggers
40411      * a toolbar update by reading the markup state of the current selection in the editor.
40412      */
40413     updateToolbar: function(){
40414
40415         if(!this.editor.activated){
40416             this.editor.onFirstFocus();
40417             return;
40418         }
40419
40420         var btns = this.tb.items.map, 
40421             doc = this.editor.doc,
40422             frameId = this.editor.frameId;
40423
40424         if(!this.disable.font && !Roo.isSafari){
40425             /*
40426             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40427             if(name != this.fontSelect.dom.value){
40428                 this.fontSelect.dom.value = name;
40429             }
40430             */
40431         }
40432         if(!this.disable.format){
40433             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40434             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40435             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40436         }
40437         if(!this.disable.alignments){
40438             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40439             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40440             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40441         }
40442         if(!Roo.isSafari && !this.disable.lists){
40443             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40444             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40445         }
40446         
40447         var ans = this.editor.getAllAncestors();
40448         if (this.formatCombo) {
40449             
40450             
40451             var store = this.formatCombo.store;
40452             this.formatCombo.setValue("");
40453             for (var i =0; i < ans.length;i++) {
40454                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40455                     // select it..
40456                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40457                     break;
40458                 }
40459             }
40460         }
40461         
40462         
40463         
40464         // hides menus... - so this cant be on a menu...
40465         Roo.menu.MenuMgr.hideAll();
40466
40467         //this.editorsyncValue();
40468     },
40469    
40470     
40471     createFontOptions : function(){
40472         var buf = [], fs = this.fontFamilies, ff, lc;
40473         for(var i = 0, len = fs.length; i< len; i++){
40474             ff = fs[i];
40475             lc = ff.toLowerCase();
40476             buf.push(
40477                 '<option value="',lc,'" style="font-family:',ff,';"',
40478                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40479                     ff,
40480                 '</option>'
40481             );
40482         }
40483         return buf.join('');
40484     },
40485     
40486     toggleSourceEdit : function(sourceEditMode){
40487         if(sourceEditMode === undefined){
40488             sourceEditMode = !this.sourceEditMode;
40489         }
40490         this.sourceEditMode = sourceEditMode === true;
40491         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40492         // just toggle the button?
40493         if(btn.pressed !== this.editor.sourceEditMode){
40494             btn.toggle(this.editor.sourceEditMode);
40495             return;
40496         }
40497         
40498         if(this.sourceEditMode){
40499             this.tb.items.each(function(item){
40500                 if(item.cmd != 'sourceedit'){
40501                     item.disable();
40502                 }
40503             });
40504           
40505         }else{
40506             if(this.initialized){
40507                 this.tb.items.each(function(item){
40508                     item.enable();
40509                 });
40510             }
40511             
40512         }
40513         // tell the editor that it's been pressed..
40514         this.editor.toggleSourceEdit(sourceEditMode);
40515        
40516     },
40517      /**
40518      * Object collection of toolbar tooltips for the buttons in the editor. The key
40519      * is the command id associated with that button and the value is a valid QuickTips object.
40520      * For example:
40521 <pre><code>
40522 {
40523     bold : {
40524         title: 'Bold (Ctrl+B)',
40525         text: 'Make the selected text bold.',
40526         cls: 'x-html-editor-tip'
40527     },
40528     italic : {
40529         title: 'Italic (Ctrl+I)',
40530         text: 'Make the selected text italic.',
40531         cls: 'x-html-editor-tip'
40532     },
40533     ...
40534 </code></pre>
40535     * @type Object
40536      */
40537     buttonTips : {
40538         bold : {
40539             title: 'Bold (Ctrl+B)',
40540             text: 'Make the selected text bold.',
40541             cls: 'x-html-editor-tip'
40542         },
40543         italic : {
40544             title: 'Italic (Ctrl+I)',
40545             text: 'Make the selected text italic.',
40546             cls: 'x-html-editor-tip'
40547         },
40548         underline : {
40549             title: 'Underline (Ctrl+U)',
40550             text: 'Underline the selected text.',
40551             cls: 'x-html-editor-tip'
40552         },
40553         increasefontsize : {
40554             title: 'Grow Text',
40555             text: 'Increase the font size.',
40556             cls: 'x-html-editor-tip'
40557         },
40558         decreasefontsize : {
40559             title: 'Shrink Text',
40560             text: 'Decrease the font size.',
40561             cls: 'x-html-editor-tip'
40562         },
40563         backcolor : {
40564             title: 'Text Highlight Color',
40565             text: 'Change the background color of the selected text.',
40566             cls: 'x-html-editor-tip'
40567         },
40568         forecolor : {
40569             title: 'Font Color',
40570             text: 'Change the color of the selected text.',
40571             cls: 'x-html-editor-tip'
40572         },
40573         justifyleft : {
40574             title: 'Align Text Left',
40575             text: 'Align text to the left.',
40576             cls: 'x-html-editor-tip'
40577         },
40578         justifycenter : {
40579             title: 'Center Text',
40580             text: 'Center text in the editor.',
40581             cls: 'x-html-editor-tip'
40582         },
40583         justifyright : {
40584             title: 'Align Text Right',
40585             text: 'Align text to the right.',
40586             cls: 'x-html-editor-tip'
40587         },
40588         insertunorderedlist : {
40589             title: 'Bullet List',
40590             text: 'Start a bulleted list.',
40591             cls: 'x-html-editor-tip'
40592         },
40593         insertorderedlist : {
40594             title: 'Numbered List',
40595             text: 'Start a numbered list.',
40596             cls: 'x-html-editor-tip'
40597         },
40598         createlink : {
40599             title: 'Hyperlink',
40600             text: 'Make the selected text a hyperlink.',
40601             cls: 'x-html-editor-tip'
40602         },
40603         sourceedit : {
40604             title: 'Source Edit',
40605             text: 'Switch to source editing mode.',
40606             cls: 'x-html-editor-tip'
40607         }
40608     },
40609     // private
40610     onDestroy : function(){
40611         if(this.rendered){
40612             
40613             this.tb.items.each(function(item){
40614                 if(item.menu){
40615                     item.menu.removeAll();
40616                     if(item.menu.el){
40617                         item.menu.el.destroy();
40618                     }
40619                 }
40620                 item.destroy();
40621             });
40622              
40623         }
40624     },
40625     onFirstFocus: function() {
40626         this.tb.items.each(function(item){
40627            item.enable();
40628         });
40629     }
40630 });
40631
40632
40633
40634
40635 // <script type="text/javascript">
40636 /*
40637  * Based on
40638  * Ext JS Library 1.1.1
40639  * Copyright(c) 2006-2007, Ext JS, LLC.
40640  *  
40641  
40642  */
40643
40644  
40645 /**
40646  * @class Roo.form.HtmlEditor.ToolbarContext
40647  * Context Toolbar
40648  * 
40649  * Usage:
40650  *
40651  new Roo.form.HtmlEditor({
40652     ....
40653     toolbars : [
40654         { xtype: 'ToolbarStandard', styles : {} }
40655         { xtype: 'ToolbarContext', disable : {} }
40656     ]
40657 })
40658
40659      
40660  * 
40661  * @config : {Object} disable List of elements to disable.. (not done yet.)
40662  * @config : {Object} styles  Map of styles available.
40663  * 
40664  */
40665
40666 Roo.form.HtmlEditor.ToolbarContext = function(config)
40667 {
40668     
40669     Roo.apply(this, config);
40670     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40671     // dont call parent... till later.
40672     this.styles = this.styles || {};
40673 }
40674 Roo.form.HtmlEditor.ToolbarContext.types = {
40675     'IMG' : {
40676         width : {
40677             title: "Width",
40678             width: 40
40679         },
40680         height:  {
40681             title: "Height",
40682             width: 40
40683         },
40684         align: {
40685             title: "Align",
40686             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40687             width : 80
40688             
40689         },
40690         border: {
40691             title: "Border",
40692             width: 40
40693         },
40694         alt: {
40695             title: "Alt",
40696             width: 120
40697         },
40698         src : {
40699             title: "Src",
40700             width: 220
40701         }
40702         
40703     },
40704     'A' : {
40705         name : {
40706             title: "Name",
40707             width: 50
40708         },
40709         href:  {
40710             title: "Href",
40711             width: 220
40712         } // border?
40713         
40714     },
40715     'TABLE' : {
40716         rows : {
40717             title: "Rows",
40718             width: 20
40719         },
40720         cols : {
40721             title: "Cols",
40722             width: 20
40723         },
40724         width : {
40725             title: "Width",
40726             width: 40
40727         },
40728         height : {
40729             title: "Height",
40730             width: 40
40731         },
40732         border : {
40733             title: "Border",
40734             width: 20
40735         }
40736     },
40737     'TD' : {
40738         width : {
40739             title: "Width",
40740             width: 40
40741         },
40742         height : {
40743             title: "Height",
40744             width: 40
40745         },   
40746         align: {
40747             title: "Align",
40748             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40749             width: 80
40750         },
40751         valign: {
40752             title: "Valign",
40753             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40754             width: 80
40755         },
40756         colspan: {
40757             title: "Colspan",
40758             width: 20
40759             
40760         }
40761     },
40762     'INPUT' : {
40763         name : {
40764             title: "name",
40765             width: 120
40766         },
40767         value : {
40768             title: "Value",
40769             width: 120
40770         },
40771         width : {
40772             title: "Width",
40773             width: 40
40774         }
40775     },
40776     'LABEL' : {
40777         'for' : {
40778             title: "For",
40779             width: 120
40780         }
40781     },
40782     'TEXTAREA' : {
40783           name : {
40784             title: "name",
40785             width: 120
40786         },
40787         rows : {
40788             title: "Rows",
40789             width: 20
40790         },
40791         cols : {
40792             title: "Cols",
40793             width: 20
40794         }
40795     },
40796     'SELECT' : {
40797         name : {
40798             title: "name",
40799             width: 120
40800         },
40801         selectoptions : {
40802             title: "Options",
40803             width: 200
40804         }
40805     },
40806     
40807     // should we really allow this??
40808     // should this just be 
40809     'BODY' : {
40810         title : {
40811             title: "title",
40812             width: 200,
40813             disabled : true
40814         }
40815     },
40816     '*' : {
40817         // empty..
40818     }
40819 };
40820
40821
40822
40823 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40824     
40825     tb: false,
40826     
40827     rendered: false,
40828     
40829     editor : false,
40830     /**
40831      * @cfg {Object} disable  List of toolbar elements to disable
40832          
40833      */
40834     disable : false,
40835     /**
40836      * @cfg {Object} styles List of styles 
40837      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40838      *
40839      * These must be defined in the page, so they get rendered correctly..
40840      * .headline { }
40841      * TD.underline { }
40842      * 
40843      */
40844     styles : false,
40845     
40846     
40847     
40848     toolbars : false,
40849     
40850     init : function(editor)
40851     {
40852         this.editor = editor;
40853         
40854         
40855         var fid = editor.frameId;
40856         var etb = this;
40857         function btn(id, toggle, handler){
40858             var xid = fid + '-'+ id ;
40859             return {
40860                 id : xid,
40861                 cmd : id,
40862                 cls : 'x-btn-icon x-edit-'+id,
40863                 enableToggle:toggle !== false,
40864                 scope: editor, // was editor...
40865                 handler:handler||editor.relayBtnCmd,
40866                 clickEvent:'mousedown',
40867                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40868                 tabIndex:-1
40869             };
40870         }
40871         // create a new element.
40872         var wdiv = editor.wrap.createChild({
40873                 tag: 'div'
40874             }, editor.wrap.dom.firstChild.nextSibling, true);
40875         
40876         // can we do this more than once??
40877         
40878          // stop form submits
40879       
40880  
40881         // disable everything...
40882         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40883         this.toolbars = {};
40884            
40885         for (var i in  ty) {
40886           
40887             this.toolbars[i] = this.buildToolbar(ty[i],i);
40888         }
40889         this.tb = this.toolbars.BODY;
40890         this.tb.el.show();
40891         this.buildFooter();
40892         this.footer.show();
40893          
40894         this.rendered = true;
40895         
40896         // the all the btns;
40897         editor.on('editorevent', this.updateToolbar, this);
40898         // other toolbars need to implement this..
40899         //editor.on('editmodechange', this.updateToolbar, this);
40900     },
40901     
40902     
40903     
40904     /**
40905      * Protected method that will not generally be called directly. It triggers
40906      * a toolbar update by reading the markup state of the current selection in the editor.
40907      */
40908     updateToolbar: function(editor,ev,sel){
40909
40910         //Roo.log(ev);
40911         // capture mouse up - this is handy for selecting images..
40912         // perhaps should go somewhere else...
40913         if(!this.editor.activated){
40914              this.editor.onFirstFocus();
40915             return;
40916         }
40917         
40918         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
40919         // selectNode - might want to handle IE?
40920         if (ev &&
40921             (ev.type == 'mouseup' || ev.type == 'click' ) &&
40922             ev.target && ev.target.tagName == 'IMG') {
40923             // they have click on an image...
40924             // let's see if we can change the selection...
40925             sel = ev.target;
40926          
40927               var nodeRange = sel.ownerDocument.createRange();
40928             try {
40929                 nodeRange.selectNode(sel);
40930             } catch (e) {
40931                 nodeRange.selectNodeContents(sel);
40932             }
40933             //nodeRange.collapse(true);
40934             var s = editor.win.getSelection();
40935             s.removeAllRanges();
40936             s.addRange(nodeRange);
40937         }  
40938         
40939       
40940         var updateFooter = sel ? false : true;
40941         
40942         
40943         var ans = this.editor.getAllAncestors();
40944         
40945         // pick
40946         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40947         
40948         if (!sel) { 
40949             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40950             sel = sel ? sel : this.editor.doc.body;
40951             sel = sel.tagName.length ? sel : this.editor.doc.body;
40952             
40953         }
40954         // pick a menu that exists..
40955         var tn = sel.tagName.toUpperCase();
40956         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40957         
40958         tn = sel.tagName.toUpperCase();
40959         
40960         var lastSel = this.tb.selectedNode
40961         
40962         this.tb.selectedNode = sel;
40963         
40964         // if current menu does not match..
40965         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40966                 
40967             this.tb.el.hide();
40968             ///console.log("show: " + tn);
40969             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40970             this.tb.el.show();
40971             // update name
40972             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40973             
40974             
40975             // update attributes
40976             if (this.tb.fields) {
40977                 this.tb.fields.each(function(e) {
40978                    e.setValue(sel.getAttribute(e.name));
40979                 });
40980             }
40981             
40982             var hasStyles = false;
40983             for(var i in this.styles) {
40984                 hasStyles = true;
40985                 break;
40986             }
40987             
40988             // update styles
40989             if (hasStyles) { 
40990                 var st = this.tb.fields.item(0);
40991                 
40992                 st.store.removeAll();
40993                
40994                 
40995                 var cn = sel.className.split(/\s+/);
40996                 
40997                 var avs = [];
40998                 if (this.styles['*']) {
40999                     
41000                     Roo.each(this.styles['*'], function(v) {
41001                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41002                     });
41003                 }
41004                 if (this.styles[tn]) { 
41005                     Roo.each(this.styles[tn], function(v) {
41006                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41007                     });
41008                 }
41009                 
41010                 st.store.loadData(avs);
41011                 st.collapse();
41012                 st.setValue(cn);
41013             }
41014             // flag our selected Node.
41015             this.tb.selectedNode = sel;
41016            
41017            
41018             Roo.menu.MenuMgr.hideAll();
41019
41020         }
41021         
41022         if (!updateFooter) {
41023             return;
41024         }
41025         // update the footer
41026         //
41027         var html = '';
41028         
41029         this.footerEls = ans.reverse();
41030         Roo.each(this.footerEls, function(a,i) {
41031             if (!a) { return; }
41032             html += html.length ? ' &gt; '  :  '';
41033             
41034             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
41035             
41036         });
41037        
41038         // 
41039         var sz = this.footDisp.up('td').getSize();
41040         this.footDisp.dom.style.width = (sz.width -10) + 'px';
41041         this.footDisp.dom.style.marginLeft = '5px';
41042         
41043         this.footDisp.dom.style.overflow = 'hidden';
41044         
41045         this.footDisp.dom.innerHTML = html;
41046             
41047         //this.editorsyncValue();
41048     },
41049    
41050        
41051     // private
41052     onDestroy : function(){
41053         if(this.rendered){
41054             
41055             this.tb.items.each(function(item){
41056                 if(item.menu){
41057                     item.menu.removeAll();
41058                     if(item.menu.el){
41059                         item.menu.el.destroy();
41060                     }
41061                 }
41062                 item.destroy();
41063             });
41064              
41065         }
41066     },
41067     onFirstFocus: function() {
41068         // need to do this for all the toolbars..
41069         this.tb.items.each(function(item){
41070            item.enable();
41071         });
41072     },
41073     buildToolbar: function(tlist, nm)
41074     {
41075         var editor = this.editor;
41076          // create a new element.
41077         var wdiv = editor.wrap.createChild({
41078                 tag: 'div'
41079             }, editor.wrap.dom.firstChild.nextSibling, true);
41080         
41081        
41082         var tb = new Roo.Toolbar(wdiv);
41083         // add the name..
41084         
41085         tb.add(nm+ ":&nbsp;");
41086         
41087         var styles = [];
41088         for(var i in this.styles) {
41089             styles.push(i);
41090         }
41091         
41092         // styles...
41093         if (styles && styles.length) {
41094             
41095             // this needs a multi-select checkbox...
41096             tb.addField( new Roo.form.ComboBox({
41097                 store: new Roo.data.SimpleStore({
41098                     id : 'val',
41099                     fields: ['val', 'selected'],
41100                     data : [] 
41101                 }),
41102                 name : 'className',
41103                 displayField:'val',
41104                 typeAhead: false,
41105                 mode: 'local',
41106                 editable : false,
41107                 triggerAction: 'all',
41108                 emptyText:'Select Style',
41109                 selectOnFocus:true,
41110                 width: 130,
41111                 listeners : {
41112                     'select': function(c, r, i) {
41113                         // initial support only for on class per el..
41114                         tb.selectedNode.className =  r ? r.get('val') : '';
41115                         editor.syncValue();
41116                     }
41117                 }
41118     
41119             }));
41120         }
41121             
41122         
41123         
41124         for (var i in tlist) {
41125             
41126             var item = tlist[i];
41127             tb.add(item.title + ":&nbsp;");
41128             
41129             
41130             
41131             
41132             if (item.opts) {
41133                 // opts == pulldown..
41134                 tb.addField( new Roo.form.ComboBox({
41135                     store: new Roo.data.SimpleStore({
41136                         id : 'val',
41137                         fields: ['val'],
41138                         data : item.opts  
41139                     }),
41140                     name : i,
41141                     displayField:'val',
41142                     typeAhead: false,
41143                     mode: 'local',
41144                     editable : false,
41145                     triggerAction: 'all',
41146                     emptyText:'Select',
41147                     selectOnFocus:true,
41148                     width: item.width ? item.width  : 130,
41149                     listeners : {
41150                         'select': function(c, r, i) {
41151                             tb.selectedNode.setAttribute(c.name, r.get('val'));
41152                         }
41153                     }
41154
41155                 }));
41156                 continue;
41157                     
41158                  
41159                 
41160                 tb.addField( new Roo.form.TextField({
41161                     name: i,
41162                     width: 100,
41163                     //allowBlank:false,
41164                     value: ''
41165                 }));
41166                 continue;
41167             }
41168             tb.addField( new Roo.form.TextField({
41169                 name: i,
41170                 width: item.width,
41171                 //allowBlank:true,
41172                 value: '',
41173                 listeners: {
41174                     'change' : function(f, nv, ov) {
41175                         tb.selectedNode.setAttribute(f.name, nv);
41176                     }
41177                 }
41178             }));
41179              
41180         }
41181         tb.el.on('click', function(e){
41182             e.preventDefault(); // what does this do?
41183         });
41184         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41185         tb.el.hide();
41186         tb.name = nm;
41187         // dont need to disable them... as they will get hidden
41188         return tb;
41189          
41190         
41191     },
41192     buildFooter : function()
41193     {
41194         
41195         var fel = this.editor.wrap.createChild();
41196         this.footer = new Roo.Toolbar(fel);
41197         // toolbar has scrolly on left / right?
41198         var footDisp= new Roo.Toolbar.Fill();
41199         var _t = this;
41200         this.footer.add(
41201             {
41202                 text : '&lt;',
41203                 xtype: 'Button',
41204                 handler : function() {
41205                     _t.footDisp.scrollTo('left',0,true)
41206                 }
41207             }
41208         );
41209         this.footer.add( footDisp );
41210         this.footer.add( 
41211             {
41212                 text : '&gt;',
41213                 xtype: 'Button',
41214                 handler : function() {
41215                     // no animation..
41216                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41217                 }
41218             }
41219         );
41220         var fel = Roo.get(footDisp.el);
41221         fel.addClass('x-editor-context');
41222         this.footDispWrap = fel; 
41223         this.footDispWrap.overflow  = 'hidden';
41224         
41225         this.footDisp = fel.createChild();
41226         this.footDispWrap.on('click', this.onContextClick, this)
41227         
41228         
41229     },
41230     onContextClick : function (ev,dom)
41231     {
41232         ev.preventDefault();
41233         var  cn = dom.className;
41234         Roo.log(cn);
41235         if (!cn.match(/x-ed-loc-/)) {
41236             return;
41237         }
41238         var n = cn.split('-').pop();
41239         var ans = this.footerEls;
41240         var sel = ans[n];
41241         
41242          // pick
41243         var range = this.editor.createRange();
41244         
41245         range.selectNodeContents(sel);
41246         //range.selectNode(sel);
41247         
41248         
41249         var selection = this.editor.getSelection();
41250         selection.removeAllRanges();
41251         selection.addRange(range);
41252         
41253         
41254         
41255         this.updateToolbar(null, null, sel);
41256         
41257         
41258     }
41259     
41260     
41261     
41262     
41263     
41264 });
41265
41266
41267
41268
41269
41270 /*
41271  * Based on:
41272  * Ext JS Library 1.1.1
41273  * Copyright(c) 2006-2007, Ext JS, LLC.
41274  *
41275  * Originally Released Under LGPL - original licence link has changed is not relivant.
41276  *
41277  * Fork - LGPL
41278  * <script type="text/javascript">
41279  */
41280  
41281 /**
41282  * @class Roo.form.BasicForm
41283  * @extends Roo.util.Observable
41284  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41285  * @constructor
41286  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41287  * @param {Object} config Configuration options
41288  */
41289 Roo.form.BasicForm = function(el, config){
41290     this.allItems = [];
41291     this.childForms = [];
41292     Roo.apply(this, config);
41293     /*
41294      * The Roo.form.Field items in this form.
41295      * @type MixedCollection
41296      */
41297      
41298      
41299     this.items = new Roo.util.MixedCollection(false, function(o){
41300         return o.id || (o.id = Roo.id());
41301     });
41302     this.addEvents({
41303         /**
41304          * @event beforeaction
41305          * Fires before any action is performed. Return false to cancel the action.
41306          * @param {Form} this
41307          * @param {Action} action The action to be performed
41308          */
41309         beforeaction: true,
41310         /**
41311          * @event actionfailed
41312          * Fires when an action fails.
41313          * @param {Form} this
41314          * @param {Action} action The action that failed
41315          */
41316         actionfailed : true,
41317         /**
41318          * @event actioncomplete
41319          * Fires when an action is completed.
41320          * @param {Form} this
41321          * @param {Action} action The action that completed
41322          */
41323         actioncomplete : true
41324     });
41325     if(el){
41326         this.initEl(el);
41327     }
41328     Roo.form.BasicForm.superclass.constructor.call(this);
41329 };
41330
41331 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41332     /**
41333      * @cfg {String} method
41334      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41335      */
41336     /**
41337      * @cfg {DataReader} reader
41338      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41339      * This is optional as there is built-in support for processing JSON.
41340      */
41341     /**
41342      * @cfg {DataReader} errorReader
41343      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41344      * This is completely optional as there is built-in support for processing JSON.
41345      */
41346     /**
41347      * @cfg {String} url
41348      * The URL to use for form actions if one isn't supplied in the action options.
41349      */
41350     /**
41351      * @cfg {Boolean} fileUpload
41352      * Set to true if this form is a file upload.
41353      */
41354      
41355     /**
41356      * @cfg {Object} baseParams
41357      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41358      */
41359      /**
41360      
41361     /**
41362      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41363      */
41364     timeout: 30,
41365
41366     // private
41367     activeAction : null,
41368
41369     /**
41370      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41371      * or setValues() data instead of when the form was first created.
41372      */
41373     trackResetOnLoad : false,
41374     
41375     
41376     /**
41377      * childForms - used for multi-tab forms
41378      * @type {Array}
41379      */
41380     childForms : false,
41381     
41382     /**
41383      * allItems - full list of fields.
41384      * @type {Array}
41385      */
41386     allItems : false,
41387     
41388     /**
41389      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41390      * element by passing it or its id or mask the form itself by passing in true.
41391      * @type Mixed
41392      */
41393     waitMsgTarget : false,
41394
41395     // private
41396     initEl : function(el){
41397         this.el = Roo.get(el);
41398         this.id = this.el.id || Roo.id();
41399         this.el.on('submit', this.onSubmit, this);
41400         this.el.addClass('x-form');
41401     },
41402
41403     // private
41404     onSubmit : function(e){
41405         e.stopEvent();
41406     },
41407
41408     /**
41409      * Returns true if client-side validation on the form is successful.
41410      * @return Boolean
41411      */
41412     isValid : function(){
41413         var valid = true;
41414         this.items.each(function(f){
41415            if(!f.validate()){
41416                valid = false;
41417            }
41418         });
41419         return valid;
41420     },
41421
41422     /**
41423      * Returns true if any fields in this form have changed since their original load.
41424      * @return Boolean
41425      */
41426     isDirty : function(){
41427         var dirty = false;
41428         this.items.each(function(f){
41429            if(f.isDirty()){
41430                dirty = true;
41431                return false;
41432            }
41433         });
41434         return dirty;
41435     },
41436
41437     /**
41438      * Performs a predefined action (submit or load) or custom actions you define on this form.
41439      * @param {String} actionName The name of the action type
41440      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41441      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41442      * accept other config options):
41443      * <pre>
41444 Property          Type             Description
41445 ----------------  ---------------  ----------------------------------------------------------------------------------
41446 url               String           The url for the action (defaults to the form's url)
41447 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41448 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41449 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41450                                    validate the form on the client (defaults to false)
41451      * </pre>
41452      * @return {BasicForm} this
41453      */
41454     doAction : function(action, options){
41455         if(typeof action == 'string'){
41456             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41457         }
41458         if(this.fireEvent('beforeaction', this, action) !== false){
41459             this.beforeAction(action);
41460             action.run.defer(100, action);
41461         }
41462         return this;
41463     },
41464
41465     /**
41466      * Shortcut to do a submit action.
41467      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41468      * @return {BasicForm} this
41469      */
41470     submit : function(options){
41471         this.doAction('submit', options);
41472         return this;
41473     },
41474
41475     /**
41476      * Shortcut to do a load action.
41477      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41478      * @return {BasicForm} this
41479      */
41480     load : function(options){
41481         this.doAction('load', options);
41482         return this;
41483     },
41484
41485     /**
41486      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41487      * @param {Record} record The record to edit
41488      * @return {BasicForm} this
41489      */
41490     updateRecord : function(record){
41491         record.beginEdit();
41492         var fs = record.fields;
41493         fs.each(function(f){
41494             var field = this.findField(f.name);
41495             if(field){
41496                 record.set(f.name, field.getValue());
41497             }
41498         }, this);
41499         record.endEdit();
41500         return this;
41501     },
41502
41503     /**
41504      * Loads an Roo.data.Record into this form.
41505      * @param {Record} record The record to load
41506      * @return {BasicForm} this
41507      */
41508     loadRecord : function(record){
41509         this.setValues(record.data);
41510         return this;
41511     },
41512
41513     // private
41514     beforeAction : function(action){
41515         var o = action.options;
41516         
41517        
41518         if(this.waitMsgTarget === true){
41519             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41520         }else if(this.waitMsgTarget){
41521             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41522             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41523         }else {
41524             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41525         }
41526          
41527     },
41528
41529     // private
41530     afterAction : function(action, success){
41531         this.activeAction = null;
41532         var o = action.options;
41533         
41534         if(this.waitMsgTarget === true){
41535             this.el.unmask();
41536         }else if(this.waitMsgTarget){
41537             this.waitMsgTarget.unmask();
41538         }else{
41539             Roo.MessageBox.updateProgress(1);
41540             Roo.MessageBox.hide();
41541         }
41542          
41543         if(success){
41544             if(o.reset){
41545                 this.reset();
41546             }
41547             Roo.callback(o.success, o.scope, [this, action]);
41548             this.fireEvent('actioncomplete', this, action);
41549             
41550         }else{
41551             
41552             // failure condition..
41553             // we have a scenario where updates need confirming.
41554             // eg. if a locking scenario exists..
41555             // we look for { errors : { needs_confirm : true }} in the response.
41556             if (
41557                 (typeof(action.result) != 'undefined')  &&
41558                 (typeof(action.result.errors) != 'undefined')  &&
41559                 (typeof(action.result.errors.needs_confirm) != 'undefined')
41560            ){
41561                 var _t = this;
41562                 Roo.MessageBox.confirm(
41563                     "Change requires confirmation",
41564                     action.result.errorMsg,
41565                     function(r) {
41566                         if (r != 'yes') {
41567                             return;
41568                         }
41569                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
41570                     }
41571                     
41572                 );
41573                 
41574                 
41575                 
41576                 return;
41577             }
41578             
41579             Roo.callback(o.failure, o.scope, [this, action]);
41580             // show an error message if no failed handler is set..
41581             if (!this.hasListener('actionfailed')) {
41582                 Roo.MessageBox.alert("Error",
41583                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41584                         action.result.errorMsg :
41585                         "Saving Failed, please check your entries or try again"
41586                 );
41587             }
41588             
41589             this.fireEvent('actionfailed', this, action);
41590         }
41591         
41592     },
41593
41594     /**
41595      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41596      * @param {String} id The value to search for
41597      * @return Field
41598      */
41599     findField : function(id){
41600         var field = this.items.get(id);
41601         if(!field){
41602             this.items.each(function(f){
41603                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41604                     field = f;
41605                     return false;
41606                 }
41607             });
41608         }
41609         return field || null;
41610     },
41611
41612     /**
41613      * Add a secondary form to this one, 
41614      * Used to provide tabbed forms. One form is primary, with hidden values 
41615      * which mirror the elements from the other forms.
41616      * 
41617      * @param {Roo.form.Form} form to add.
41618      * 
41619      */
41620     addForm : function(form)
41621     {
41622        
41623         if (this.childForms.indexOf(form) > -1) {
41624             // already added..
41625             return;
41626         }
41627         this.childForms.push(form);
41628         var n = '';
41629         Roo.each(form.allItems, function (fe) {
41630             
41631             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41632             if (this.findField(n)) { // already added..
41633                 return;
41634             }
41635             var add = new Roo.form.Hidden({
41636                 name : n
41637             });
41638             add.render(this.el);
41639             
41640             this.add( add );
41641         }, this);
41642         
41643     },
41644     /**
41645      * Mark fields in this form invalid in bulk.
41646      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41647      * @return {BasicForm} this
41648      */
41649     markInvalid : function(errors){
41650         if(errors instanceof Array){
41651             for(var i = 0, len = errors.length; i < len; i++){
41652                 var fieldError = errors[i];
41653                 var f = this.findField(fieldError.id);
41654                 if(f){
41655                     f.markInvalid(fieldError.msg);
41656                 }
41657             }
41658         }else{
41659             var field, id;
41660             for(id in errors){
41661                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41662                     field.markInvalid(errors[id]);
41663                 }
41664             }
41665         }
41666         Roo.each(this.childForms || [], function (f) {
41667             f.markInvalid(errors);
41668         });
41669         
41670         return this;
41671     },
41672
41673     /**
41674      * Set values for fields in this form in bulk.
41675      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41676      * @return {BasicForm} this
41677      */
41678     setValues : function(values){
41679         if(values instanceof Array){ // array of objects
41680             for(var i = 0, len = values.length; i < len; i++){
41681                 var v = values[i];
41682                 var f = this.findField(v.id);
41683                 if(f){
41684                     f.setValue(v.value);
41685                     if(this.trackResetOnLoad){
41686                         f.originalValue = f.getValue();
41687                     }
41688                 }
41689             }
41690         }else{ // object hash
41691             var field, id;
41692             for(id in values){
41693                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41694                     
41695                     if (field.setFromData && 
41696                         field.valueField && 
41697                         field.displayField &&
41698                         // combos' with local stores can 
41699                         // be queried via setValue()
41700                         // to set their value..
41701                         (field.store && !field.store.isLocal)
41702                         ) {
41703                         // it's a combo
41704                         var sd = { };
41705                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41706                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41707                         field.setFromData(sd);
41708                         
41709                     } else {
41710                         field.setValue(values[id]);
41711                     }
41712                     
41713                     
41714                     if(this.trackResetOnLoad){
41715                         field.originalValue = field.getValue();
41716                     }
41717                 }
41718             }
41719         }
41720          
41721         Roo.each(this.childForms || [], function (f) {
41722             f.setValues(values);
41723         });
41724                 
41725         return this;
41726     },
41727
41728     /**
41729      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41730      * they are returned as an array.
41731      * @param {Boolean} asString
41732      * @return {Object}
41733      */
41734     getValues : function(asString){
41735         if (this.childForms) {
41736             // copy values from the child forms
41737             Roo.each(this.childForms, function (f) {
41738                 this.setValues(f.getValues());
41739             }, this);
41740         }
41741         
41742         
41743         
41744         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41745         if(asString === true){
41746             return fs;
41747         }
41748         return Roo.urlDecode(fs);
41749     },
41750     
41751     /**
41752      * Returns the fields in this form as an object with key/value pairs. 
41753      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41754      * @return {Object}
41755      */
41756     getFieldValues : function(with_hidden)
41757     {
41758         if (this.childForms) {
41759             // copy values from the child forms
41760             // should this call getFieldValues - probably not as we do not currently copy
41761             // hidden fields when we generate..
41762             Roo.each(this.childForms, function (f) {
41763                 this.setValues(f.getValues());
41764             }, this);
41765         }
41766         
41767         var ret = {};
41768         this.items.each(function(f){
41769             if (!f.getName()) {
41770                 return;
41771             }
41772             var v = f.getValue();
41773             // not sure if this supported any more..
41774             if ((typeof(v) == 'object') && f.getRawValue) {
41775                 v = f.getRawValue() ; // dates..
41776             }
41777             // combo boxes where name != hiddenName...
41778             if (f.name != f.getName()) {
41779                 ret[f.name] = f.getRawValue();
41780             }
41781             ret[f.getName()] = v;
41782         });
41783         
41784         return ret;
41785     },
41786
41787     /**
41788      * Clears all invalid messages in this form.
41789      * @return {BasicForm} this
41790      */
41791     clearInvalid : function(){
41792         this.items.each(function(f){
41793            f.clearInvalid();
41794         });
41795         
41796         Roo.each(this.childForms || [], function (f) {
41797             f.clearInvalid();
41798         });
41799         
41800         
41801         return this;
41802     },
41803
41804     /**
41805      * Resets this form.
41806      * @return {BasicForm} this
41807      */
41808     reset : function(){
41809         this.items.each(function(f){
41810             f.reset();
41811         });
41812         
41813         Roo.each(this.childForms || [], function (f) {
41814             f.reset();
41815         });
41816        
41817         
41818         return this;
41819     },
41820
41821     /**
41822      * Add Roo.form components to this form.
41823      * @param {Field} field1
41824      * @param {Field} field2 (optional)
41825      * @param {Field} etc (optional)
41826      * @return {BasicForm} this
41827      */
41828     add : function(){
41829         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41830         return this;
41831     },
41832
41833
41834     /**
41835      * Removes a field from the items collection (does NOT remove its markup).
41836      * @param {Field} field
41837      * @return {BasicForm} this
41838      */
41839     remove : function(field){
41840         this.items.remove(field);
41841         return this;
41842     },
41843
41844     /**
41845      * Looks at the fields in this form, checks them for an id attribute,
41846      * and calls applyTo on the existing dom element with that id.
41847      * @return {BasicForm} this
41848      */
41849     render : function(){
41850         this.items.each(function(f){
41851             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41852                 f.applyTo(f.id);
41853             }
41854         });
41855         return this;
41856     },
41857
41858     /**
41859      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41860      * @param {Object} values
41861      * @return {BasicForm} this
41862      */
41863     applyToFields : function(o){
41864         this.items.each(function(f){
41865            Roo.apply(f, o);
41866         });
41867         return this;
41868     },
41869
41870     /**
41871      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41872      * @param {Object} values
41873      * @return {BasicForm} this
41874      */
41875     applyIfToFields : function(o){
41876         this.items.each(function(f){
41877            Roo.applyIf(f, o);
41878         });
41879         return this;
41880     }
41881 });
41882
41883 // back compat
41884 Roo.BasicForm = Roo.form.BasicForm;/*
41885  * Based on:
41886  * Ext JS Library 1.1.1
41887  * Copyright(c) 2006-2007, Ext JS, LLC.
41888  *
41889  * Originally Released Under LGPL - original licence link has changed is not relivant.
41890  *
41891  * Fork - LGPL
41892  * <script type="text/javascript">
41893  */
41894
41895 /**
41896  * @class Roo.form.Form
41897  * @extends Roo.form.BasicForm
41898  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41899  * @constructor
41900  * @param {Object} config Configuration options
41901  */
41902 Roo.form.Form = function(config){
41903     var xitems =  [];
41904     if (config.items) {
41905         xitems = config.items;
41906         delete config.items;
41907     }
41908    
41909     
41910     Roo.form.Form.superclass.constructor.call(this, null, config);
41911     this.url = this.url || this.action;
41912     if(!this.root){
41913         this.root = new Roo.form.Layout(Roo.applyIf({
41914             id: Roo.id()
41915         }, config));
41916     }
41917     this.active = this.root;
41918     /**
41919      * Array of all the buttons that have been added to this form via {@link addButton}
41920      * @type Array
41921      */
41922     this.buttons = [];
41923     this.allItems = [];
41924     this.addEvents({
41925         /**
41926          * @event clientvalidation
41927          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41928          * @param {Form} this
41929          * @param {Boolean} valid true if the form has passed client-side validation
41930          */
41931         clientvalidation: true,
41932         /**
41933          * @event rendered
41934          * Fires when the form is rendered
41935          * @param {Roo.form.Form} form
41936          */
41937         rendered : true
41938     });
41939     
41940     if (this.progressUrl) {
41941             // push a hidden field onto the list of fields..
41942             this.addxtype( {
41943                     xns: Roo.form, 
41944                     xtype : 'Hidden', 
41945                     name : 'UPLOAD_IDENTIFIER' 
41946             });
41947         }
41948         
41949     
41950     Roo.each(xitems, this.addxtype, this);
41951     
41952     
41953     
41954 };
41955
41956 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41957     /**
41958      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41959      */
41960     /**
41961      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41962      */
41963     /**
41964      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41965      */
41966     buttonAlign:'center',
41967
41968     /**
41969      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41970      */
41971     minButtonWidth:75,
41972
41973     /**
41974      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41975      * This property cascades to child containers if not set.
41976      */
41977     labelAlign:'left',
41978
41979     /**
41980      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41981      * fires a looping event with that state. This is required to bind buttons to the valid
41982      * state using the config value formBind:true on the button.
41983      */
41984     monitorValid : false,
41985
41986     /**
41987      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41988      */
41989     monitorPoll : 200,
41990     
41991     /**
41992      * @cfg {String} progressUrl - Url to return progress data 
41993      */
41994     
41995     progressUrl : false,
41996   
41997     /**
41998      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41999      * fields are added and the column is closed. If no fields are passed the column remains open
42000      * until end() is called.
42001      * @param {Object} config The config to pass to the column
42002      * @param {Field} field1 (optional)
42003      * @param {Field} field2 (optional)
42004      * @param {Field} etc (optional)
42005      * @return Column The column container object
42006      */
42007     column : function(c){
42008         var col = new Roo.form.Column(c);
42009         this.start(col);
42010         if(arguments.length > 1){ // duplicate code required because of Opera
42011             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42012             this.end();
42013         }
42014         return col;
42015     },
42016
42017     /**
42018      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
42019      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
42020      * until end() is called.
42021      * @param {Object} config The config to pass to the fieldset
42022      * @param {Field} field1 (optional)
42023      * @param {Field} field2 (optional)
42024      * @param {Field} etc (optional)
42025      * @return FieldSet The fieldset container object
42026      */
42027     fieldset : function(c){
42028         var fs = new Roo.form.FieldSet(c);
42029         this.start(fs);
42030         if(arguments.length > 1){ // duplicate code required because of Opera
42031             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42032             this.end();
42033         }
42034         return fs;
42035     },
42036
42037     /**
42038      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
42039      * fields are added and the container is closed. If no fields are passed the container remains open
42040      * until end() is called.
42041      * @param {Object} config The config to pass to the Layout
42042      * @param {Field} field1 (optional)
42043      * @param {Field} field2 (optional)
42044      * @param {Field} etc (optional)
42045      * @return Layout The container object
42046      */
42047     container : function(c){
42048         var l = new Roo.form.Layout(c);
42049         this.start(l);
42050         if(arguments.length > 1){ // duplicate code required because of Opera
42051             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42052             this.end();
42053         }
42054         return l;
42055     },
42056
42057     /**
42058      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
42059      * @param {Object} container A Roo.form.Layout or subclass of Layout
42060      * @return {Form} this
42061      */
42062     start : function(c){
42063         // cascade label info
42064         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
42065         this.active.stack.push(c);
42066         c.ownerCt = this.active;
42067         this.active = c;
42068         return this;
42069     },
42070
42071     /**
42072      * Closes the current open container
42073      * @return {Form} this
42074      */
42075     end : function(){
42076         if(this.active == this.root){
42077             return this;
42078         }
42079         this.active = this.active.ownerCt;
42080         return this;
42081     },
42082
42083     /**
42084      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
42085      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
42086      * as the label of the field.
42087      * @param {Field} field1
42088      * @param {Field} field2 (optional)
42089      * @param {Field} etc. (optional)
42090      * @return {Form} this
42091      */
42092     add : function(){
42093         this.active.stack.push.apply(this.active.stack, arguments);
42094         this.allItems.push.apply(this.allItems,arguments);
42095         var r = [];
42096         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
42097             if(a[i].isFormField){
42098                 r.push(a[i]);
42099             }
42100         }
42101         if(r.length > 0){
42102             Roo.form.Form.superclass.add.apply(this, r);
42103         }
42104         return this;
42105     },
42106     
42107
42108     
42109     
42110     
42111      /**
42112      * Find any element that has been added to a form, using it's ID or name
42113      * This can include framesets, columns etc. along with regular fields..
42114      * @param {String} id - id or name to find.
42115      
42116      * @return {Element} e - or false if nothing found.
42117      */
42118     findbyId : function(id)
42119     {
42120         var ret = false;
42121         if (!id) {
42122             return ret;
42123         }
42124         Roo.each(this.allItems, function(f){
42125             if (f.id == id || f.name == id ){
42126                 ret = f;
42127                 return false;
42128             }
42129         });
42130         return ret;
42131     },
42132
42133     
42134     
42135     /**
42136      * Render this form into the passed container. This should only be called once!
42137      * @param {String/HTMLElement/Element} container The element this component should be rendered into
42138      * @return {Form} this
42139      */
42140     render : function(ct)
42141     {
42142         
42143         
42144         
42145         ct = Roo.get(ct);
42146         var o = this.autoCreate || {
42147             tag: 'form',
42148             method : this.method || 'POST',
42149             id : this.id || Roo.id()
42150         };
42151         this.initEl(ct.createChild(o));
42152
42153         this.root.render(this.el);
42154         
42155        
42156              
42157         this.items.each(function(f){
42158             f.render('x-form-el-'+f.id);
42159         });
42160
42161         if(this.buttons.length > 0){
42162             // tables are required to maintain order and for correct IE layout
42163             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
42164                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
42165                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
42166             }}, null, true);
42167             var tr = tb.getElementsByTagName('tr')[0];
42168             for(var i = 0, len = this.buttons.length; i < len; i++) {
42169                 var b = this.buttons[i];
42170                 var td = document.createElement('td');
42171                 td.className = 'x-form-btn-td';
42172                 b.render(tr.appendChild(td));
42173             }
42174         }
42175         if(this.monitorValid){ // initialize after render
42176             this.startMonitoring();
42177         }
42178         this.fireEvent('rendered', this);
42179         return this;
42180     },
42181
42182     /**
42183      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42184      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42185      * object or a valid Roo.DomHelper element config
42186      * @param {Function} handler The function called when the button is clicked
42187      * @param {Object} scope (optional) The scope of the handler function
42188      * @return {Roo.Button}
42189      */
42190     addButton : function(config, handler, scope){
42191         var bc = {
42192             handler: handler,
42193             scope: scope,
42194             minWidth: this.minButtonWidth,
42195             hideParent:true
42196         };
42197         if(typeof config == "string"){
42198             bc.text = config;
42199         }else{
42200             Roo.apply(bc, config);
42201         }
42202         var btn = new Roo.Button(null, bc);
42203         this.buttons.push(btn);
42204         return btn;
42205     },
42206
42207      /**
42208      * Adds a series of form elements (using the xtype property as the factory method.
42209      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42210      * @param {Object} config 
42211      */
42212     
42213     addxtype : function()
42214     {
42215         var ar = Array.prototype.slice.call(arguments, 0);
42216         var ret = false;
42217         for(var i = 0; i < ar.length; i++) {
42218             if (!ar[i]) {
42219                 continue; // skip -- if this happends something invalid got sent, we 
42220                 // should ignore it, as basically that interface element will not show up
42221                 // and that should be pretty obvious!!
42222             }
42223             
42224             if (Roo.form[ar[i].xtype]) {
42225                 ar[i].form = this;
42226                 var fe = Roo.factory(ar[i], Roo.form);
42227                 if (!ret) {
42228                     ret = fe;
42229                 }
42230                 fe.form = this;
42231                 if (fe.store) {
42232                     fe.store.form = this;
42233                 }
42234                 if (fe.isLayout) {  
42235                          
42236                     this.start(fe);
42237                     this.allItems.push(fe);
42238                     if (fe.items && fe.addxtype) {
42239                         fe.addxtype.apply(fe, fe.items);
42240                         delete fe.items;
42241                     }
42242                      this.end();
42243                     continue;
42244                 }
42245                 
42246                 
42247                  
42248                 this.add(fe);
42249               //  console.log('adding ' + ar[i].xtype);
42250             }
42251             if (ar[i].xtype == 'Button') {  
42252                 //console.log('adding button');
42253                 //console.log(ar[i]);
42254                 this.addButton(ar[i]);
42255                 this.allItems.push(fe);
42256                 continue;
42257             }
42258             
42259             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42260                 alert('end is not supported on xtype any more, use items');
42261             //    this.end();
42262             //    //console.log('adding end');
42263             }
42264             
42265         }
42266         return ret;
42267     },
42268     
42269     /**
42270      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42271      * option "monitorValid"
42272      */
42273     startMonitoring : function(){
42274         if(!this.bound){
42275             this.bound = true;
42276             Roo.TaskMgr.start({
42277                 run : this.bindHandler,
42278                 interval : this.monitorPoll || 200,
42279                 scope: this
42280             });
42281         }
42282     },
42283
42284     /**
42285      * Stops monitoring of the valid state of this form
42286      */
42287     stopMonitoring : function(){
42288         this.bound = false;
42289     },
42290
42291     // private
42292     bindHandler : function(){
42293         if(!this.bound){
42294             return false; // stops binding
42295         }
42296         var valid = true;
42297         this.items.each(function(f){
42298             if(!f.isValid(true)){
42299                 valid = false;
42300                 return false;
42301             }
42302         });
42303         for(var i = 0, len = this.buttons.length; i < len; i++){
42304             var btn = this.buttons[i];
42305             if(btn.formBind === true && btn.disabled === valid){
42306                 btn.setDisabled(!valid);
42307             }
42308         }
42309         this.fireEvent('clientvalidation', this, valid);
42310     }
42311     
42312     
42313     
42314     
42315     
42316     
42317     
42318     
42319 });
42320
42321
42322 // back compat
42323 Roo.Form = Roo.form.Form;
42324 /*
42325  * Based on:
42326  * Ext JS Library 1.1.1
42327  * Copyright(c) 2006-2007, Ext JS, LLC.
42328  *
42329  * Originally Released Under LGPL - original licence link has changed is not relivant.
42330  *
42331  * Fork - LGPL
42332  * <script type="text/javascript">
42333  */
42334  
42335  /**
42336  * @class Roo.form.Action
42337  * Internal Class used to handle form actions
42338  * @constructor
42339  * @param {Roo.form.BasicForm} el The form element or its id
42340  * @param {Object} config Configuration options
42341  */
42342  
42343  
42344 // define the action interface
42345 Roo.form.Action = function(form, options){
42346     this.form = form;
42347     this.options = options || {};
42348 };
42349 /**
42350  * Client Validation Failed
42351  * @const 
42352  */
42353 Roo.form.Action.CLIENT_INVALID = 'client';
42354 /**
42355  * Server Validation Failed
42356  * @const 
42357  */
42358  Roo.form.Action.SERVER_INVALID = 'server';
42359  /**
42360  * Connect to Server Failed
42361  * @const 
42362  */
42363 Roo.form.Action.CONNECT_FAILURE = 'connect';
42364 /**
42365  * Reading Data from Server Failed
42366  * @const 
42367  */
42368 Roo.form.Action.LOAD_FAILURE = 'load';
42369
42370 Roo.form.Action.prototype = {
42371     type : 'default',
42372     failureType : undefined,
42373     response : undefined,
42374     result : undefined,
42375
42376     // interface method
42377     run : function(options){
42378
42379     },
42380
42381     // interface method
42382     success : function(response){
42383
42384     },
42385
42386     // interface method
42387     handleResponse : function(response){
42388
42389     },
42390
42391     // default connection failure
42392     failure : function(response){
42393         
42394         this.response = response;
42395         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42396         this.form.afterAction(this, false);
42397     },
42398
42399     processResponse : function(response){
42400         this.response = response;
42401         if(!response.responseText){
42402             return true;
42403         }
42404         this.result = this.handleResponse(response);
42405         return this.result;
42406     },
42407
42408     // utility functions used internally
42409     getUrl : function(appendParams){
42410         var url = this.options.url || this.form.url || this.form.el.dom.action;
42411         if(appendParams){
42412             var p = this.getParams();
42413             if(p){
42414                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42415             }
42416         }
42417         return url;
42418     },
42419
42420     getMethod : function(){
42421         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42422     },
42423
42424     getParams : function(){
42425         var bp = this.form.baseParams;
42426         var p = this.options.params;
42427         if(p){
42428             if(typeof p == "object"){
42429                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42430             }else if(typeof p == 'string' && bp){
42431                 p += '&' + Roo.urlEncode(bp);
42432             }
42433         }else if(bp){
42434             p = Roo.urlEncode(bp);
42435         }
42436         return p;
42437     },
42438
42439     createCallback : function(){
42440         return {
42441             success: this.success,
42442             failure: this.failure,
42443             scope: this,
42444             timeout: (this.form.timeout*1000),
42445             upload: this.form.fileUpload ? this.success : undefined
42446         };
42447     }
42448 };
42449
42450 Roo.form.Action.Submit = function(form, options){
42451     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42452 };
42453
42454 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42455     type : 'submit',
42456
42457     haveProgress : false,
42458     uploadComplete : false,
42459     
42460     // uploadProgress indicator.
42461     uploadProgress : function()
42462     {
42463         if (!this.form.progressUrl) {
42464             return;
42465         }
42466         
42467         if (!this.haveProgress) {
42468             Roo.MessageBox.progress("Uploading", "Uploading");
42469         }
42470         if (this.uploadComplete) {
42471            Roo.MessageBox.hide();
42472            return;
42473         }
42474         
42475         this.haveProgress = true;
42476    
42477         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42478         
42479         var c = new Roo.data.Connection();
42480         c.request({
42481             url : this.form.progressUrl,
42482             params: {
42483                 id : uid
42484             },
42485             method: 'GET',
42486             success : function(req){
42487                //console.log(data);
42488                 var rdata = false;
42489                 var edata;
42490                 try  {
42491                    rdata = Roo.decode(req.responseText)
42492                 } catch (e) {
42493                     Roo.log("Invalid data from server..");
42494                     Roo.log(edata);
42495                     return;
42496                 }
42497                 if (!rdata || !rdata.success) {
42498                     Roo.log(rdata);
42499                     return;
42500                 }
42501                 var data = rdata.data;
42502                 
42503                 if (this.uploadComplete) {
42504                    Roo.MessageBox.hide();
42505                    return;
42506                 }
42507                    
42508                 if (data){
42509                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42510                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42511                     );
42512                 }
42513                 this.uploadProgress.defer(2000,this);
42514             },
42515        
42516             failure: function(data) {
42517                 Roo.log('progress url failed ');
42518                 Roo.log(data);
42519             },
42520             scope : this
42521         });
42522            
42523     },
42524     
42525     
42526     run : function()
42527     {
42528         // run get Values on the form, so it syncs any secondary forms.
42529         this.form.getValues();
42530         
42531         var o = this.options;
42532         var method = this.getMethod();
42533         var isPost = method == 'POST';
42534         if(o.clientValidation === false || this.form.isValid()){
42535             
42536             if (this.form.progressUrl) {
42537                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42538                     (new Date() * 1) + '' + Math.random());
42539                     
42540             } 
42541             
42542             
42543             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42544                 form:this.form.el.dom,
42545                 url:this.getUrl(!isPost),
42546                 method: method,
42547                 params:isPost ? this.getParams() : null,
42548                 isUpload: this.form.fileUpload
42549             }));
42550             
42551             this.uploadProgress();
42552
42553         }else if (o.clientValidation !== false){ // client validation failed
42554             this.failureType = Roo.form.Action.CLIENT_INVALID;
42555             this.form.afterAction(this, false);
42556         }
42557     },
42558
42559     success : function(response)
42560     {
42561         this.uploadComplete= true;
42562         if (this.haveProgress) {
42563             Roo.MessageBox.hide();
42564         }
42565         
42566         
42567         var result = this.processResponse(response);
42568         if(result === true || result.success){
42569             this.form.afterAction(this, true);
42570             return;
42571         }
42572         if(result.errors){
42573             this.form.markInvalid(result.errors);
42574             this.failureType = Roo.form.Action.SERVER_INVALID;
42575         }
42576         this.form.afterAction(this, false);
42577     },
42578     failure : function(response)
42579     {
42580         this.uploadComplete= true;
42581         if (this.haveProgress) {
42582             Roo.MessageBox.hide();
42583         }
42584         
42585         this.response = response;
42586         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42587         this.form.afterAction(this, false);
42588     },
42589     
42590     handleResponse : function(response){
42591         if(this.form.errorReader){
42592             var rs = this.form.errorReader.read(response);
42593             var errors = [];
42594             if(rs.records){
42595                 for(var i = 0, len = rs.records.length; i < len; i++) {
42596                     var r = rs.records[i];
42597                     errors[i] = r.data;
42598                 }
42599             }
42600             if(errors.length < 1){
42601                 errors = null;
42602             }
42603             return {
42604                 success : rs.success,
42605                 errors : errors
42606             };
42607         }
42608         var ret = false;
42609         try {
42610             ret = Roo.decode(response.responseText);
42611         } catch (e) {
42612             ret = {
42613                 success: false,
42614                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42615                 errors : []
42616             };
42617         }
42618         return ret;
42619         
42620     }
42621 });
42622
42623
42624 Roo.form.Action.Load = function(form, options){
42625     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42626     this.reader = this.form.reader;
42627 };
42628
42629 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42630     type : 'load',
42631
42632     run : function(){
42633         
42634         Roo.Ajax.request(Roo.apply(
42635                 this.createCallback(), {
42636                     method:this.getMethod(),
42637                     url:this.getUrl(false),
42638                     params:this.getParams()
42639         }));
42640     },
42641
42642     success : function(response){
42643         
42644         var result = this.processResponse(response);
42645         if(result === true || !result.success || !result.data){
42646             this.failureType = Roo.form.Action.LOAD_FAILURE;
42647             this.form.afterAction(this, false);
42648             return;
42649         }
42650         this.form.clearInvalid();
42651         this.form.setValues(result.data);
42652         this.form.afterAction(this, true);
42653     },
42654
42655     handleResponse : function(response){
42656         if(this.form.reader){
42657             var rs = this.form.reader.read(response);
42658             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42659             return {
42660                 success : rs.success,
42661                 data : data
42662             };
42663         }
42664         return Roo.decode(response.responseText);
42665     }
42666 });
42667
42668 Roo.form.Action.ACTION_TYPES = {
42669     'load' : Roo.form.Action.Load,
42670     'submit' : Roo.form.Action.Submit
42671 };/*
42672  * Based on:
42673  * Ext JS Library 1.1.1
42674  * Copyright(c) 2006-2007, Ext JS, LLC.
42675  *
42676  * Originally Released Under LGPL - original licence link has changed is not relivant.
42677  *
42678  * Fork - LGPL
42679  * <script type="text/javascript">
42680  */
42681  
42682 /**
42683  * @class Roo.form.Layout
42684  * @extends Roo.Component
42685  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42686  * @constructor
42687  * @param {Object} config Configuration options
42688  */
42689 Roo.form.Layout = function(config){
42690     var xitems = [];
42691     if (config.items) {
42692         xitems = config.items;
42693         delete config.items;
42694     }
42695     Roo.form.Layout.superclass.constructor.call(this, config);
42696     this.stack = [];
42697     Roo.each(xitems, this.addxtype, this);
42698      
42699 };
42700
42701 Roo.extend(Roo.form.Layout, Roo.Component, {
42702     /**
42703      * @cfg {String/Object} autoCreate
42704      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42705      */
42706     /**
42707      * @cfg {String/Object/Function} style
42708      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42709      * a function which returns such a specification.
42710      */
42711     /**
42712      * @cfg {String} labelAlign
42713      * Valid values are "left," "top" and "right" (defaults to "left")
42714      */
42715     /**
42716      * @cfg {Number} labelWidth
42717      * Fixed width in pixels of all field labels (defaults to undefined)
42718      */
42719     /**
42720      * @cfg {Boolean} clear
42721      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42722      */
42723     clear : true,
42724     /**
42725      * @cfg {String} labelSeparator
42726      * The separator to use after field labels (defaults to ':')
42727      */
42728     labelSeparator : ':',
42729     /**
42730      * @cfg {Boolean} hideLabels
42731      * True to suppress the display of field labels in this layout (defaults to false)
42732      */
42733     hideLabels : false,
42734
42735     // private
42736     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42737     
42738     isLayout : true,
42739     
42740     // private
42741     onRender : function(ct, position){
42742         if(this.el){ // from markup
42743             this.el = Roo.get(this.el);
42744         }else {  // generate
42745             var cfg = this.getAutoCreate();
42746             this.el = ct.createChild(cfg, position);
42747         }
42748         if(this.style){
42749             this.el.applyStyles(this.style);
42750         }
42751         if(this.labelAlign){
42752             this.el.addClass('x-form-label-'+this.labelAlign);
42753         }
42754         if(this.hideLabels){
42755             this.labelStyle = "display:none";
42756             this.elementStyle = "padding-left:0;";
42757         }else{
42758             if(typeof this.labelWidth == 'number'){
42759                 this.labelStyle = "width:"+this.labelWidth+"px;";
42760                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42761             }
42762             if(this.labelAlign == 'top'){
42763                 this.labelStyle = "width:auto;";
42764                 this.elementStyle = "padding-left:0;";
42765             }
42766         }
42767         var stack = this.stack;
42768         var slen = stack.length;
42769         if(slen > 0){
42770             if(!this.fieldTpl){
42771                 var t = new Roo.Template(
42772                     '<div class="x-form-item {5}">',
42773                         '<label for="{0}" style="{2}">{1}{4}</label>',
42774                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42775                         '</div>',
42776                     '</div><div class="x-form-clear-left"></div>'
42777                 );
42778                 t.disableFormats = true;
42779                 t.compile();
42780                 Roo.form.Layout.prototype.fieldTpl = t;
42781             }
42782             for(var i = 0; i < slen; i++) {
42783                 if(stack[i].isFormField){
42784                     this.renderField(stack[i]);
42785                 }else{
42786                     this.renderComponent(stack[i]);
42787                 }
42788             }
42789         }
42790         if(this.clear){
42791             this.el.createChild({cls:'x-form-clear'});
42792         }
42793     },
42794
42795     // private
42796     renderField : function(f){
42797         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42798                f.id, //0
42799                f.fieldLabel, //1
42800                f.labelStyle||this.labelStyle||'', //2
42801                this.elementStyle||'', //3
42802                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42803                f.itemCls||this.itemCls||''  //5
42804        ], true).getPrevSibling());
42805     },
42806
42807     // private
42808     renderComponent : function(c){
42809         c.render(c.isLayout ? this.el : this.el.createChild());    
42810     },
42811     /**
42812      * Adds a object form elements (using the xtype property as the factory method.)
42813      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42814      * @param {Object} config 
42815      */
42816     addxtype : function(o)
42817     {
42818         // create the lement.
42819         o.form = this.form;
42820         var fe = Roo.factory(o, Roo.form);
42821         this.form.allItems.push(fe);
42822         this.stack.push(fe);
42823         
42824         if (fe.isFormField) {
42825             this.form.items.add(fe);
42826         }
42827          
42828         return fe;
42829     }
42830 });
42831
42832 /**
42833  * @class Roo.form.Column
42834  * @extends Roo.form.Layout
42835  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42836  * @constructor
42837  * @param {Object} config Configuration options
42838  */
42839 Roo.form.Column = function(config){
42840     Roo.form.Column.superclass.constructor.call(this, config);
42841 };
42842
42843 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42844     /**
42845      * @cfg {Number/String} width
42846      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42847      */
42848     /**
42849      * @cfg {String/Object} autoCreate
42850      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42851      */
42852
42853     // private
42854     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42855
42856     // private
42857     onRender : function(ct, position){
42858         Roo.form.Column.superclass.onRender.call(this, ct, position);
42859         if(this.width){
42860             this.el.setWidth(this.width);
42861         }
42862     }
42863 });
42864
42865
42866 /**
42867  * @class Roo.form.Row
42868  * @extends Roo.form.Layout
42869  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42870  * @constructor
42871  * @param {Object} config Configuration options
42872  */
42873
42874  
42875 Roo.form.Row = function(config){
42876     Roo.form.Row.superclass.constructor.call(this, config);
42877 };
42878  
42879 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42880       /**
42881      * @cfg {Number/String} width
42882      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42883      */
42884     /**
42885      * @cfg {Number/String} height
42886      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42887      */
42888     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42889     
42890     padWidth : 20,
42891     // private
42892     onRender : function(ct, position){
42893         //console.log('row render');
42894         if(!this.rowTpl){
42895             var t = new Roo.Template(
42896                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42897                     '<label for="{0}" style="{2}">{1}{4}</label>',
42898                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42899                     '</div>',
42900                 '</div>'
42901             );
42902             t.disableFormats = true;
42903             t.compile();
42904             Roo.form.Layout.prototype.rowTpl = t;
42905         }
42906         this.fieldTpl = this.rowTpl;
42907         
42908         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42909         var labelWidth = 100;
42910         
42911         if ((this.labelAlign != 'top')) {
42912             if (typeof this.labelWidth == 'number') {
42913                 labelWidth = this.labelWidth
42914             }
42915             this.padWidth =  20 + labelWidth;
42916             
42917         }
42918         
42919         Roo.form.Column.superclass.onRender.call(this, ct, position);
42920         if(this.width){
42921             this.el.setWidth(this.width);
42922         }
42923         if(this.height){
42924             this.el.setHeight(this.height);
42925         }
42926     },
42927     
42928     // private
42929     renderField : function(f){
42930         f.fieldEl = this.fieldTpl.append(this.el, [
42931                f.id, f.fieldLabel,
42932                f.labelStyle||this.labelStyle||'',
42933                this.elementStyle||'',
42934                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42935                f.itemCls||this.itemCls||'',
42936                f.width ? f.width + this.padWidth : 160 + this.padWidth
42937        ],true);
42938     }
42939 });
42940  
42941
42942 /**
42943  * @class Roo.form.FieldSet
42944  * @extends Roo.form.Layout
42945  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42946  * @constructor
42947  * @param {Object} config Configuration options
42948  */
42949 Roo.form.FieldSet = function(config){
42950     Roo.form.FieldSet.superclass.constructor.call(this, config);
42951 };
42952
42953 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42954     /**
42955      * @cfg {String} legend
42956      * The text to display as the legend for the FieldSet (defaults to '')
42957      */
42958     /**
42959      * @cfg {String/Object} autoCreate
42960      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42961      */
42962
42963     // private
42964     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42965
42966     // private
42967     onRender : function(ct, position){
42968         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42969         if(this.legend){
42970             this.setLegend(this.legend);
42971         }
42972     },
42973
42974     // private
42975     setLegend : function(text){
42976         if(this.rendered){
42977             this.el.child('legend').update(text);
42978         }
42979     }
42980 });/*
42981  * Based on:
42982  * Ext JS Library 1.1.1
42983  * Copyright(c) 2006-2007, Ext JS, LLC.
42984  *
42985  * Originally Released Under LGPL - original licence link has changed is not relivant.
42986  *
42987  * Fork - LGPL
42988  * <script type="text/javascript">
42989  */
42990 /**
42991  * @class Roo.form.VTypes
42992  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42993  * @singleton
42994  */
42995 Roo.form.VTypes = function(){
42996     // closure these in so they are only created once.
42997     var alpha = /^[a-zA-Z_]+$/;
42998     var alphanum = /^[a-zA-Z0-9_]+$/;
42999     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
43000     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
43001
43002     // All these messages and functions are configurable
43003     return {
43004         /**
43005          * The function used to validate email addresses
43006          * @param {String} value The email address
43007          */
43008         'email' : function(v){
43009             return email.test(v);
43010         },
43011         /**
43012          * The error text to display when the email validation function returns false
43013          * @type String
43014          */
43015         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
43016         /**
43017          * The keystroke filter mask to be applied on email input
43018          * @type RegExp
43019          */
43020         'emailMask' : /[a-z0-9_\.\-@]/i,
43021
43022         /**
43023          * The function used to validate URLs
43024          * @param {String} value The URL
43025          */
43026         'url' : function(v){
43027             return url.test(v);
43028         },
43029         /**
43030          * The error text to display when the url validation function returns false
43031          * @type String
43032          */
43033         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
43034         
43035         /**
43036          * The function used to validate alpha values
43037          * @param {String} value The value
43038          */
43039         'alpha' : function(v){
43040             return alpha.test(v);
43041         },
43042         /**
43043          * The error text to display when the alpha validation function returns false
43044          * @type String
43045          */
43046         'alphaText' : 'This field should only contain letters and _',
43047         /**
43048          * The keystroke filter mask to be applied on alpha input
43049          * @type RegExp
43050          */
43051         'alphaMask' : /[a-z_]/i,
43052
43053         /**
43054          * The function used to validate alphanumeric values
43055          * @param {String} value The value
43056          */
43057         'alphanum' : function(v){
43058             return alphanum.test(v);
43059         },
43060         /**
43061          * The error text to display when the alphanumeric validation function returns false
43062          * @type String
43063          */
43064         'alphanumText' : 'This field should only contain letters, numbers and _',
43065         /**
43066          * The keystroke filter mask to be applied on alphanumeric input
43067          * @type RegExp
43068          */
43069         'alphanumMask' : /[a-z0-9_]/i
43070     };
43071 }();//<script type="text/javascript">
43072
43073 /**
43074  * @class Roo.form.FCKeditor
43075  * @extends Roo.form.TextArea
43076  * Wrapper around the FCKEditor http://www.fckeditor.net
43077  * @constructor
43078  * Creates a new FCKeditor
43079  * @param {Object} config Configuration options
43080  */
43081 Roo.form.FCKeditor = function(config){
43082     Roo.form.FCKeditor.superclass.constructor.call(this, config);
43083     this.addEvents({
43084          /**
43085          * @event editorinit
43086          * Fired when the editor is initialized - you can add extra handlers here..
43087          * @param {FCKeditor} this
43088          * @param {Object} the FCK object.
43089          */
43090         editorinit : true
43091     });
43092     
43093     
43094 };
43095 Roo.form.FCKeditor.editors = { };
43096 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
43097 {
43098     //defaultAutoCreate : {
43099     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
43100     //},
43101     // private
43102     /**
43103      * @cfg {Object} fck options - see fck manual for details.
43104      */
43105     fckconfig : false,
43106     
43107     /**
43108      * @cfg {Object} fck toolbar set (Basic or Default)
43109      */
43110     toolbarSet : 'Basic',
43111     /**
43112      * @cfg {Object} fck BasePath
43113      */ 
43114     basePath : '/fckeditor/',
43115     
43116     
43117     frame : false,
43118     
43119     value : '',
43120     
43121    
43122     onRender : function(ct, position)
43123     {
43124         if(!this.el){
43125             this.defaultAutoCreate = {
43126                 tag: "textarea",
43127                 style:"width:300px;height:60px;",
43128                 autocomplete: "off"
43129             };
43130         }
43131         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
43132         /*
43133         if(this.grow){
43134             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
43135             if(this.preventScrollbars){
43136                 this.el.setStyle("overflow", "hidden");
43137             }
43138             this.el.setHeight(this.growMin);
43139         }
43140         */
43141         //console.log('onrender' + this.getId() );
43142         Roo.form.FCKeditor.editors[this.getId()] = this;
43143          
43144
43145         this.replaceTextarea() ;
43146         
43147     },
43148     
43149     getEditor : function() {
43150         return this.fckEditor;
43151     },
43152     /**
43153      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43154      * @param {Mixed} value The value to set
43155      */
43156     
43157     
43158     setValue : function(value)
43159     {
43160         //console.log('setValue: ' + value);
43161         
43162         if(typeof(value) == 'undefined') { // not sure why this is happending...
43163             return;
43164         }
43165         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43166         
43167         //if(!this.el || !this.getEditor()) {
43168         //    this.value = value;
43169             //this.setValue.defer(100,this,[value]);    
43170         //    return;
43171         //} 
43172         
43173         if(!this.getEditor()) {
43174             return;
43175         }
43176         
43177         this.getEditor().SetData(value);
43178         
43179         //
43180
43181     },
43182
43183     /**
43184      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43185      * @return {Mixed} value The field value
43186      */
43187     getValue : function()
43188     {
43189         
43190         if (this.frame && this.frame.dom.style.display == 'none') {
43191             return Roo.form.FCKeditor.superclass.getValue.call(this);
43192         }
43193         
43194         if(!this.el || !this.getEditor()) {
43195            
43196            // this.getValue.defer(100,this); 
43197             return this.value;
43198         }
43199        
43200         
43201         var value=this.getEditor().GetData();
43202         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43203         return Roo.form.FCKeditor.superclass.getValue.call(this);
43204         
43205
43206     },
43207
43208     /**
43209      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43210      * @return {Mixed} value The field value
43211      */
43212     getRawValue : function()
43213     {
43214         if (this.frame && this.frame.dom.style.display == 'none') {
43215             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43216         }
43217         
43218         if(!this.el || !this.getEditor()) {
43219             //this.getRawValue.defer(100,this); 
43220             return this.value;
43221             return;
43222         }
43223         
43224         
43225         
43226         var value=this.getEditor().GetData();
43227         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43228         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43229          
43230     },
43231     
43232     setSize : function(w,h) {
43233         
43234         
43235         
43236         //if (this.frame && this.frame.dom.style.display == 'none') {
43237         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43238         //    return;
43239         //}
43240         //if(!this.el || !this.getEditor()) {
43241         //    this.setSize.defer(100,this, [w,h]); 
43242         //    return;
43243         //}
43244         
43245         
43246         
43247         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43248         
43249         this.frame.dom.setAttribute('width', w);
43250         this.frame.dom.setAttribute('height', h);
43251         this.frame.setSize(w,h);
43252         
43253     },
43254     
43255     toggleSourceEdit : function(value) {
43256         
43257       
43258          
43259         this.el.dom.style.display = value ? '' : 'none';
43260         this.frame.dom.style.display = value ?  'none' : '';
43261         
43262     },
43263     
43264     
43265     focus: function(tag)
43266     {
43267         if (this.frame.dom.style.display == 'none') {
43268             return Roo.form.FCKeditor.superclass.focus.call(this);
43269         }
43270         if(!this.el || !this.getEditor()) {
43271             this.focus.defer(100,this, [tag]); 
43272             return;
43273         }
43274         
43275         
43276         
43277         
43278         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43279         this.getEditor().Focus();
43280         if (tgs.length) {
43281             if (!this.getEditor().Selection.GetSelection()) {
43282                 this.focus.defer(100,this, [tag]); 
43283                 return;
43284             }
43285             
43286             
43287             var r = this.getEditor().EditorDocument.createRange();
43288             r.setStart(tgs[0],0);
43289             r.setEnd(tgs[0],0);
43290             this.getEditor().Selection.GetSelection().removeAllRanges();
43291             this.getEditor().Selection.GetSelection().addRange(r);
43292             this.getEditor().Focus();
43293         }
43294         
43295     },
43296     
43297     
43298     
43299     replaceTextarea : function()
43300     {
43301         if ( document.getElementById( this.getId() + '___Frame' ) )
43302             return ;
43303         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43304         //{
43305             // We must check the elements firstly using the Id and then the name.
43306         var oTextarea = document.getElementById( this.getId() );
43307         
43308         var colElementsByName = document.getElementsByName( this.getId() ) ;
43309          
43310         oTextarea.style.display = 'none' ;
43311
43312         if ( oTextarea.tabIndex ) {            
43313             this.TabIndex = oTextarea.tabIndex ;
43314         }
43315         
43316         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43317         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43318         this.frame = Roo.get(this.getId() + '___Frame')
43319     },
43320     
43321     _getConfigHtml : function()
43322     {
43323         var sConfig = '' ;
43324
43325         for ( var o in this.fckconfig ) {
43326             sConfig += sConfig.length > 0  ? '&amp;' : '';
43327             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43328         }
43329
43330         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43331     },
43332     
43333     
43334     _getIFrameHtml : function()
43335     {
43336         var sFile = 'fckeditor.html' ;
43337         /* no idea what this is about..
43338         try
43339         {
43340             if ( (/fcksource=true/i).test( window.top.location.search ) )
43341                 sFile = 'fckeditor.original.html' ;
43342         }
43343         catch (e) { 
43344         */
43345
43346         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43347         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43348         
43349         
43350         var html = '<iframe id="' + this.getId() +
43351             '___Frame" src="' + sLink +
43352             '" width="' + this.width +
43353             '" height="' + this.height + '"' +
43354             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43355             ' frameborder="0" scrolling="no"></iframe>' ;
43356
43357         return html ;
43358     },
43359     
43360     _insertHtmlBefore : function( html, element )
43361     {
43362         if ( element.insertAdjacentHTML )       {
43363             // IE
43364             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43365         } else { // Gecko
43366             var oRange = document.createRange() ;
43367             oRange.setStartBefore( element ) ;
43368             var oFragment = oRange.createContextualFragment( html );
43369             element.parentNode.insertBefore( oFragment, element ) ;
43370         }
43371     }
43372     
43373     
43374   
43375     
43376     
43377     
43378     
43379
43380 });
43381
43382 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43383
43384 function FCKeditor_OnComplete(editorInstance){
43385     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43386     f.fckEditor = editorInstance;
43387     //console.log("loaded");
43388     f.fireEvent('editorinit', f, editorInstance);
43389
43390   
43391
43392  
43393
43394
43395
43396
43397
43398
43399
43400
43401
43402
43403
43404
43405
43406
43407
43408 //<script type="text/javascript">
43409 /**
43410  * @class Roo.form.GridField
43411  * @extends Roo.form.Field
43412  * Embed a grid (or editable grid into a form)
43413  * STATUS ALPHA
43414  * 
43415  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43416  * it needs 
43417  * xgrid.store = Roo.data.Store
43418  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43419  * xgrid.store.reader = Roo.data.JsonReader 
43420  * 
43421  * 
43422  * @constructor
43423  * Creates a new GridField
43424  * @param {Object} config Configuration options
43425  */
43426 Roo.form.GridField = function(config){
43427     Roo.form.GridField.superclass.constructor.call(this, config);
43428      
43429 };
43430
43431 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43432     /**
43433      * @cfg {Number} width  - used to restrict width of grid..
43434      */
43435     width : 100,
43436     /**
43437      * @cfg {Number} height - used to restrict height of grid..
43438      */
43439     height : 50,
43440      /**
43441      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43442          * 
43443          *}
43444      */
43445     xgrid : false, 
43446     /**
43447      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43448      * {tag: "input", type: "checkbox", autocomplete: "off"})
43449      */
43450    // defaultAutoCreate : { tag: 'div' },
43451     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43452     /**
43453      * @cfg {String} addTitle Text to include for adding a title.
43454      */
43455     addTitle : false,
43456     //
43457     onResize : function(){
43458         Roo.form.Field.superclass.onResize.apply(this, arguments);
43459     },
43460
43461     initEvents : function(){
43462         // Roo.form.Checkbox.superclass.initEvents.call(this);
43463         // has no events...
43464        
43465     },
43466
43467
43468     getResizeEl : function(){
43469         return this.wrap;
43470     },
43471
43472     getPositionEl : function(){
43473         return this.wrap;
43474     },
43475
43476     // private
43477     onRender : function(ct, position){
43478         
43479         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43480         var style = this.style;
43481         delete this.style;
43482         
43483         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43484         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43485         this.viewEl = this.wrap.createChild({ tag: 'div' });
43486         if (style) {
43487             this.viewEl.applyStyles(style);
43488         }
43489         if (this.width) {
43490             this.viewEl.setWidth(this.width);
43491         }
43492         if (this.height) {
43493             this.viewEl.setHeight(this.height);
43494         }
43495         //if(this.inputValue !== undefined){
43496         //this.setValue(this.value);
43497         
43498         
43499         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43500         
43501         
43502         this.grid.render();
43503         this.grid.getDataSource().on('remove', this.refreshValue, this);
43504         this.grid.getDataSource().on('update', this.refreshValue, this);
43505         this.grid.on('afteredit', this.refreshValue, this);
43506  
43507     },
43508      
43509     
43510     /**
43511      * Sets the value of the item. 
43512      * @param {String} either an object  or a string..
43513      */
43514     setValue : function(v){
43515         //this.value = v;
43516         v = v || []; // empty set..
43517         // this does not seem smart - it really only affects memoryproxy grids..
43518         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43519             var ds = this.grid.getDataSource();
43520             // assumes a json reader..
43521             var data = {}
43522             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43523             ds.loadData( data);
43524         }
43525         // clear selection so it does not get stale.
43526         if (this.grid.sm) { 
43527             this.grid.sm.clearSelections();
43528         }
43529         
43530         Roo.form.GridField.superclass.setValue.call(this, v);
43531         this.refreshValue();
43532         // should load data in the grid really....
43533     },
43534     
43535     // private
43536     refreshValue: function() {
43537          var val = [];
43538         this.grid.getDataSource().each(function(r) {
43539             val.push(r.data);
43540         });
43541         this.el.dom.value = Roo.encode(val);
43542     }
43543     
43544      
43545     
43546     
43547 });/*
43548  * Based on:
43549  * Ext JS Library 1.1.1
43550  * Copyright(c) 2006-2007, Ext JS, LLC.
43551  *
43552  * Originally Released Under LGPL - original licence link has changed is not relivant.
43553  *
43554  * Fork - LGPL
43555  * <script type="text/javascript">
43556  */
43557 /**
43558  * @class Roo.form.DisplayField
43559  * @extends Roo.form.Field
43560  * A generic Field to display non-editable data.
43561  * @constructor
43562  * Creates a new Display Field item.
43563  * @param {Object} config Configuration options
43564  */
43565 Roo.form.DisplayField = function(config){
43566     Roo.form.DisplayField.superclass.constructor.call(this, config);
43567     
43568 };
43569
43570 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43571     inputType:      'hidden',
43572     allowBlank:     true,
43573     readOnly:         true,
43574     
43575  
43576     /**
43577      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43578      */
43579     focusClass : undefined,
43580     /**
43581      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43582      */
43583     fieldClass: 'x-form-field',
43584     
43585      /**
43586      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43587      */
43588     valueRenderer: undefined,
43589     
43590     width: 100,
43591     /**
43592      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43593      * {tag: "input", type: "checkbox", autocomplete: "off"})
43594      */
43595      
43596  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43597
43598     onResize : function(){
43599         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43600         
43601     },
43602
43603     initEvents : function(){
43604         // Roo.form.Checkbox.superclass.initEvents.call(this);
43605         // has no events...
43606        
43607     },
43608
43609
43610     getResizeEl : function(){
43611         return this.wrap;
43612     },
43613
43614     getPositionEl : function(){
43615         return this.wrap;
43616     },
43617
43618     // private
43619     onRender : function(ct, position){
43620         
43621         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43622         //if(this.inputValue !== undefined){
43623         this.wrap = this.el.wrap();
43624         
43625         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43626         
43627         if (this.bodyStyle) {
43628             this.viewEl.applyStyles(this.bodyStyle);
43629         }
43630         //this.viewEl.setStyle('padding', '2px');
43631         
43632         this.setValue(this.value);
43633         
43634     },
43635 /*
43636     // private
43637     initValue : Roo.emptyFn,
43638
43639   */
43640
43641         // private
43642     onClick : function(){
43643         
43644     },
43645
43646     /**
43647      * Sets the checked state of the checkbox.
43648      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43649      */
43650     setValue : function(v){
43651         this.value = v;
43652         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43653         // this might be called before we have a dom element..
43654         if (!this.viewEl) {
43655             return;
43656         }
43657         this.viewEl.dom.innerHTML = html;
43658         Roo.form.DisplayField.superclass.setValue.call(this, v);
43659
43660     }
43661 });/*
43662  * 
43663  * Licence- LGPL
43664  * 
43665  */
43666
43667 /**
43668  * @class Roo.form.DayPicker
43669  * @extends Roo.form.Field
43670  * A Day picker show [M] [T] [W] ....
43671  * @constructor
43672  * Creates a new Day Picker
43673  * @param {Object} config Configuration options
43674  */
43675 Roo.form.DayPicker= function(config){
43676     Roo.form.DayPicker.superclass.constructor.call(this, config);
43677      
43678 };
43679
43680 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43681     /**
43682      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43683      */
43684     focusClass : undefined,
43685     /**
43686      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43687      */
43688     fieldClass: "x-form-field",
43689    
43690     /**
43691      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43692      * {tag: "input", type: "checkbox", autocomplete: "off"})
43693      */
43694     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43695     
43696    
43697     actionMode : 'viewEl', 
43698     //
43699     // private
43700  
43701     inputType : 'hidden',
43702     
43703      
43704     inputElement: false, // real input element?
43705     basedOn: false, // ????
43706     
43707     isFormField: true, // not sure where this is needed!!!!
43708
43709     onResize : function(){
43710         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43711         if(!this.boxLabel){
43712             this.el.alignTo(this.wrap, 'c-c');
43713         }
43714     },
43715
43716     initEvents : function(){
43717         Roo.form.Checkbox.superclass.initEvents.call(this);
43718         this.el.on("click", this.onClick,  this);
43719         this.el.on("change", this.onClick,  this);
43720     },
43721
43722
43723     getResizeEl : function(){
43724         return this.wrap;
43725     },
43726
43727     getPositionEl : function(){
43728         return this.wrap;
43729     },
43730
43731     
43732     // private
43733     onRender : function(ct, position){
43734         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43735        
43736         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43737         
43738         var r1 = '<table><tr>';
43739         var r2 = '<tr class="x-form-daypick-icons">';
43740         for (var i=0; i < 7; i++) {
43741             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43742             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43743         }
43744         
43745         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43746         viewEl.select('img').on('click', this.onClick, this);
43747         this.viewEl = viewEl;   
43748         
43749         
43750         // this will not work on Chrome!!!
43751         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43752         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43753         
43754         
43755           
43756
43757     },
43758
43759     // private
43760     initValue : Roo.emptyFn,
43761
43762     /**
43763      * Returns the checked state of the checkbox.
43764      * @return {Boolean} True if checked, else false
43765      */
43766     getValue : function(){
43767         return this.el.dom.value;
43768         
43769     },
43770
43771         // private
43772     onClick : function(e){ 
43773         //this.setChecked(!this.checked);
43774         Roo.get(e.target).toggleClass('x-menu-item-checked');
43775         this.refreshValue();
43776         //if(this.el.dom.checked != this.checked){
43777         //    this.setValue(this.el.dom.checked);
43778        // }
43779     },
43780     
43781     // private
43782     refreshValue : function()
43783     {
43784         var val = '';
43785         this.viewEl.select('img',true).each(function(e,i,n)  {
43786             val += e.is(".x-menu-item-checked") ? String(n) : '';
43787         });
43788         this.setValue(val, true);
43789     },
43790
43791     /**
43792      * Sets the checked state of the checkbox.
43793      * On is always based on a string comparison between inputValue and the param.
43794      * @param {Boolean/String} value - the value to set 
43795      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43796      */
43797     setValue : function(v,suppressEvent){
43798         if (!this.el.dom) {
43799             return;
43800         }
43801         var old = this.el.dom.value ;
43802         this.el.dom.value = v;
43803         if (suppressEvent) {
43804             return ;
43805         }
43806          
43807         // update display..
43808         this.viewEl.select('img',true).each(function(e,i,n)  {
43809             
43810             var on = e.is(".x-menu-item-checked");
43811             var newv = v.indexOf(String(n)) > -1;
43812             if (on != newv) {
43813                 e.toggleClass('x-menu-item-checked');
43814             }
43815             
43816         });
43817         
43818         
43819         this.fireEvent('change', this, v, old);
43820         
43821         
43822     },
43823    
43824     // handle setting of hidden value by some other method!!?!?
43825     setFromHidden: function()
43826     {
43827         if(!this.el){
43828             return;
43829         }
43830         //console.log("SET FROM HIDDEN");
43831         //alert('setFrom hidden');
43832         this.setValue(this.el.dom.value);
43833     },
43834     
43835     onDestroy : function()
43836     {
43837         if(this.viewEl){
43838             Roo.get(this.viewEl).remove();
43839         }
43840          
43841         Roo.form.DayPicker.superclass.onDestroy.call(this);
43842     }
43843
43844 });/*
43845  * RooJS Library 1.1.1
43846  * Copyright(c) 2008-2011  Alan Knowles
43847  *
43848  * License - LGPL
43849  */
43850  
43851
43852 /**
43853  * @class Roo.form.ComboCheck
43854  * @extends Roo.form.ComboBox
43855  * A combobox for multiple select items.
43856  *
43857  * FIXME - could do with a reset button..
43858  * 
43859  * @constructor
43860  * Create a new ComboCheck
43861  * @param {Object} config Configuration options
43862  */
43863 Roo.form.ComboCheck = function(config){
43864     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43865     // should verify some data...
43866     // like
43867     // hiddenName = required..
43868     // displayField = required
43869     // valudField == required
43870     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43871     var _t = this;
43872     Roo.each(req, function(e) {
43873         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43874             throw "Roo.form.ComboCheck : missing value for: " + e;
43875         }
43876     });
43877     
43878     
43879 };
43880
43881 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43882      
43883      
43884     editable : false,
43885      
43886     selectedClass: 'x-menu-item-checked', 
43887     
43888     // private
43889     onRender : function(ct, position){
43890         var _t = this;
43891         
43892         
43893         
43894         if(!this.tpl){
43895             var cls = 'x-combo-list';
43896
43897             
43898             this.tpl =  new Roo.Template({
43899                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43900                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43901                    '<span>{' + this.displayField + '}</span>' +
43902                     '</div>' 
43903                 
43904             });
43905         }
43906  
43907         
43908         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43909         this.view.singleSelect = false;
43910         this.view.multiSelect = true;
43911         this.view.toggleSelect = true;
43912         this.pageTb.add(new Roo.Toolbar.Fill(), {
43913             
43914             text: 'Done',
43915             handler: function()
43916             {
43917                 _t.collapse();
43918             }
43919         });
43920     },
43921     
43922     onViewOver : function(e, t){
43923         // do nothing...
43924         return;
43925         
43926     },
43927     
43928     onViewClick : function(doFocus,index){
43929         return;
43930         
43931     },
43932     select: function () {
43933         //Roo.log("SELECT CALLED");
43934     },
43935      
43936     selectByValue : function(xv, scrollIntoView){
43937         var ar = this.getValueArray();
43938         var sels = [];
43939         
43940         Roo.each(ar, function(v) {
43941             if(v === undefined || v === null){
43942                 return;
43943             }
43944             var r = this.findRecord(this.valueField, v);
43945             if(r){
43946                 sels.push(this.store.indexOf(r))
43947                 
43948             }
43949         },this);
43950         this.view.select(sels);
43951         return false;
43952     },
43953     
43954     
43955     
43956     onSelect : function(record, index){
43957        // Roo.log("onselect Called");
43958        // this is only called by the clear button now..
43959         this.view.clearSelections();
43960         this.setValue('[]');
43961         if (this.value != this.valueBefore) {
43962             this.fireEvent('change', this, this.value, this.valueBefore);
43963         }
43964     },
43965     getValueArray : function()
43966     {
43967         var ar = [] ;
43968         
43969         try {
43970             //Roo.log(this.value);
43971             if (typeof(this.value) == 'undefined') {
43972                 return [];
43973             }
43974             var ar = Roo.decode(this.value);
43975             return  ar instanceof Array ? ar : []; //?? valid?
43976             
43977         } catch(e) {
43978             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43979             return [];
43980         }
43981          
43982     },
43983     expand : function ()
43984     {
43985         Roo.form.ComboCheck.superclass.expand.call(this);
43986         this.valueBefore = this.value;
43987         
43988
43989     },
43990     
43991     collapse : function(){
43992         Roo.form.ComboCheck.superclass.collapse.call(this);
43993         var sl = this.view.getSelectedIndexes();
43994         var st = this.store;
43995         var nv = [];
43996         var tv = [];
43997         var r;
43998         Roo.each(sl, function(i) {
43999             r = st.getAt(i);
44000             nv.push(r.get(this.valueField));
44001         },this);
44002         this.setValue(Roo.encode(nv));
44003         if (this.value != this.valueBefore) {
44004
44005             this.fireEvent('change', this, this.value, this.valueBefore);
44006         }
44007         
44008     },
44009     
44010     setValue : function(v){
44011         // Roo.log(v);
44012         this.value = v;
44013         
44014         var vals = this.getValueArray();
44015         var tv = [];
44016         Roo.each(vals, function(k) {
44017             var r = this.findRecord(this.valueField, k);
44018             if(r){
44019                 tv.push(r.data[this.displayField]);
44020             }else if(this.valueNotFoundText !== undefined){
44021                 tv.push( this.valueNotFoundText );
44022             }
44023         },this);
44024        // Roo.log(tv);
44025         
44026         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
44027         this.hiddenField.value = v;
44028         this.value = v;
44029     }
44030     
44031 });//<script type="text/javasscript">
44032  
44033
44034 /**
44035  * @class Roo.DDView
44036  * A DnD enabled version of Roo.View.
44037  * @param {Element/String} container The Element in which to create the View.
44038  * @param {String} tpl The template string used to create the markup for each element of the View
44039  * @param {Object} config The configuration properties. These include all the config options of
44040  * {@link Roo.View} plus some specific to this class.<br>
44041  * <p>
44042  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
44043  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
44044  * <p>
44045  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
44046 .x-view-drag-insert-above {
44047         border-top:1px dotted #3366cc;
44048 }
44049 .x-view-drag-insert-below {
44050         border-bottom:1px dotted #3366cc;
44051 }
44052 </code></pre>
44053  * 
44054  */
44055  
44056 Roo.DDView = function(container, tpl, config) {
44057     Roo.DDView.superclass.constructor.apply(this, arguments);
44058     this.getEl().setStyle("outline", "0px none");
44059     this.getEl().unselectable();
44060     if (this.dragGroup) {
44061                 this.setDraggable(this.dragGroup.split(","));
44062     }
44063     if (this.dropGroup) {
44064                 this.setDroppable(this.dropGroup.split(","));
44065     }
44066     if (this.deletable) {
44067         this.setDeletable();
44068     }
44069     this.isDirtyFlag = false;
44070         this.addEvents({
44071                 "drop" : true
44072         });
44073 };
44074
44075 Roo.extend(Roo.DDView, Roo.View, {
44076 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
44077 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
44078 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
44079 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
44080
44081         isFormField: true,
44082
44083         reset: Roo.emptyFn,
44084         
44085         clearInvalid: Roo.form.Field.prototype.clearInvalid,
44086
44087         validate: function() {
44088                 return true;
44089         },
44090         
44091         destroy: function() {
44092                 this.purgeListeners();
44093                 this.getEl.removeAllListeners();
44094                 this.getEl().remove();
44095                 if (this.dragZone) {
44096                         if (this.dragZone.destroy) {
44097                                 this.dragZone.destroy();
44098                         }
44099                 }
44100                 if (this.dropZone) {
44101                         if (this.dropZone.destroy) {
44102                                 this.dropZone.destroy();
44103                         }
44104                 }
44105         },
44106
44107 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
44108         getName: function() {
44109                 return this.name;
44110         },
44111
44112 /**     Loads the View from a JSON string representing the Records to put into the Store. */
44113         setValue: function(v) {
44114                 if (!this.store) {
44115                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
44116                 }
44117                 var data = {};
44118                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
44119                 this.store.proxy = new Roo.data.MemoryProxy(data);
44120                 this.store.load();
44121         },
44122
44123 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
44124         getValue: function() {
44125                 var result = '(';
44126                 this.store.each(function(rec) {
44127                         result += rec.id + ',';
44128                 });
44129                 return result.substr(0, result.length - 1) + ')';
44130         },
44131         
44132         getIds: function() {
44133                 var i = 0, result = new Array(this.store.getCount());
44134                 this.store.each(function(rec) {
44135                         result[i++] = rec.id;
44136                 });
44137                 return result;
44138         },
44139         
44140         isDirty: function() {
44141                 return this.isDirtyFlag;
44142         },
44143
44144 /**
44145  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
44146  *      whole Element becomes the target, and this causes the drop gesture to append.
44147  */
44148     getTargetFromEvent : function(e) {
44149                 var target = e.getTarget();
44150                 while ((target !== null) && (target.parentNode != this.el.dom)) {
44151                 target = target.parentNode;
44152                 }
44153                 if (!target) {
44154                         target = this.el.dom.lastChild || this.el.dom;
44155                 }
44156                 return target;
44157     },
44158
44159 /**
44160  *      Create the drag data which consists of an object which has the property "ddel" as
44161  *      the drag proxy element. 
44162  */
44163     getDragData : function(e) {
44164         var target = this.findItemFromChild(e.getTarget());
44165                 if(target) {
44166                         this.handleSelection(e);
44167                         var selNodes = this.getSelectedNodes();
44168             var dragData = {
44169                 source: this,
44170                 copy: this.copy || (this.allowCopy && e.ctrlKey),
44171                 nodes: selNodes,
44172                 records: []
44173                         };
44174                         var selectedIndices = this.getSelectedIndexes();
44175                         for (var i = 0; i < selectedIndices.length; i++) {
44176                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
44177                         }
44178                         if (selNodes.length == 1) {
44179                                 dragData.ddel = target.cloneNode(true); // the div element
44180                         } else {
44181                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44182                                 div.className = 'multi-proxy';
44183                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44184                                         div.appendChild(selNodes[i].cloneNode(true));
44185                                 }
44186                                 dragData.ddel = div;
44187                         }
44188             //console.log(dragData)
44189             //console.log(dragData.ddel.innerHTML)
44190                         return dragData;
44191                 }
44192         //console.log('nodragData')
44193                 return false;
44194     },
44195     
44196 /**     Specify to which ddGroup items in this DDView may be dragged. */
44197     setDraggable: function(ddGroup) {
44198         if (ddGroup instanceof Array) {
44199                 Roo.each(ddGroup, this.setDraggable, this);
44200                 return;
44201         }
44202         if (this.dragZone) {
44203                 this.dragZone.addToGroup(ddGroup);
44204         } else {
44205                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44206                                 containerScroll: true,
44207                                 ddGroup: ddGroup 
44208
44209                         });
44210 //                      Draggability implies selection. DragZone's mousedown selects the element.
44211                         if (!this.multiSelect) { this.singleSelect = true; }
44212
44213 //                      Wire the DragZone's handlers up to methods in *this*
44214                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44215                 }
44216     },
44217
44218 /**     Specify from which ddGroup this DDView accepts drops. */
44219     setDroppable: function(ddGroup) {
44220         if (ddGroup instanceof Array) {
44221                 Roo.each(ddGroup, this.setDroppable, this);
44222                 return;
44223         }
44224         if (this.dropZone) {
44225                 this.dropZone.addToGroup(ddGroup);
44226         } else {
44227                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44228                                 containerScroll: true,
44229                                 ddGroup: ddGroup
44230                         });
44231
44232 //                      Wire the DropZone's handlers up to methods in *this*
44233                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44234                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44235                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44236                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44237                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44238                 }
44239     },
44240
44241 /**     Decide whether to drop above or below a View node. */
44242     getDropPoint : function(e, n, dd){
44243         if (n == this.el.dom) { return "above"; }
44244                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44245                 var c = t + (b - t) / 2;
44246                 var y = Roo.lib.Event.getPageY(e);
44247                 if(y <= c) {
44248                         return "above";
44249                 }else{
44250                         return "below";
44251                 }
44252     },
44253
44254     onNodeEnter : function(n, dd, e, data){
44255                 return false;
44256     },
44257     
44258     onNodeOver : function(n, dd, e, data){
44259                 var pt = this.getDropPoint(e, n, dd);
44260                 // set the insert point style on the target node
44261                 var dragElClass = this.dropNotAllowed;
44262                 if (pt) {
44263                         var targetElClass;
44264                         if (pt == "above"){
44265                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44266                                 targetElClass = "x-view-drag-insert-above";
44267                         } else {
44268                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44269                                 targetElClass = "x-view-drag-insert-below";
44270                         }
44271                         if (this.lastInsertClass != targetElClass){
44272                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44273                                 this.lastInsertClass = targetElClass;
44274                         }
44275                 }
44276                 return dragElClass;
44277         },
44278
44279     onNodeOut : function(n, dd, e, data){
44280                 this.removeDropIndicators(n);
44281     },
44282
44283     onNodeDrop : function(n, dd, e, data){
44284         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44285                 return false;
44286         }
44287         var pt = this.getDropPoint(e, n, dd);
44288                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44289                 if (pt == "below") { insertAt++; }
44290                 for (var i = 0; i < data.records.length; i++) {
44291                         var r = data.records[i];
44292                         var dup = this.store.getById(r.id);
44293                         if (dup && (dd != this.dragZone)) {
44294                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44295                         } else {
44296                                 if (data.copy) {
44297                                         this.store.insert(insertAt++, r.copy());
44298                                 } else {
44299                                         data.source.isDirtyFlag = true;
44300                                         r.store.remove(r);
44301                                         this.store.insert(insertAt++, r);
44302                                 }
44303                                 this.isDirtyFlag = true;
44304                         }
44305                 }
44306                 this.dragZone.cachedTarget = null;
44307                 return true;
44308     },
44309
44310     removeDropIndicators : function(n){
44311                 if(n){
44312                         Roo.fly(n).removeClass([
44313                                 "x-view-drag-insert-above",
44314                                 "x-view-drag-insert-below"]);
44315                         this.lastInsertClass = "_noclass";
44316                 }
44317     },
44318
44319 /**
44320  *      Utility method. Add a delete option to the DDView's context menu.
44321  *      @param {String} imageUrl The URL of the "delete" icon image.
44322  */
44323         setDeletable: function(imageUrl) {
44324                 if (!this.singleSelect && !this.multiSelect) {
44325                         this.singleSelect = true;
44326                 }
44327                 var c = this.getContextMenu();
44328                 this.contextMenu.on("itemclick", function(item) {
44329                         switch (item.id) {
44330                                 case "delete":
44331                                         this.remove(this.getSelectedIndexes());
44332                                         break;
44333                         }
44334                 }, this);
44335                 this.contextMenu.add({
44336                         icon: imageUrl,
44337                         id: "delete",
44338                         text: 'Delete'
44339                 });
44340         },
44341         
44342 /**     Return the context menu for this DDView. */
44343         getContextMenu: function() {
44344                 if (!this.contextMenu) {
44345 //                      Create the View's context menu
44346                         this.contextMenu = new Roo.menu.Menu({
44347                                 id: this.id + "-contextmenu"
44348                         });
44349                         this.el.on("contextmenu", this.showContextMenu, this);
44350                 }
44351                 return this.contextMenu;
44352         },
44353         
44354         disableContextMenu: function() {
44355                 if (this.contextMenu) {
44356                         this.el.un("contextmenu", this.showContextMenu, this);
44357                 }
44358         },
44359
44360         showContextMenu: function(e, item) {
44361         item = this.findItemFromChild(e.getTarget());
44362                 if (item) {
44363                         e.stopEvent();
44364                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44365                         this.contextMenu.showAt(e.getXY());
44366             }
44367     },
44368
44369 /**
44370  *      Remove {@link Roo.data.Record}s at the specified indices.
44371  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44372  */
44373     remove: function(selectedIndices) {
44374                 selectedIndices = [].concat(selectedIndices);
44375                 for (var i = 0; i < selectedIndices.length; i++) {
44376                         var rec = this.store.getAt(selectedIndices[i]);
44377                         this.store.remove(rec);
44378                 }
44379     },
44380
44381 /**
44382  *      Double click fires the event, but also, if this is draggable, and there is only one other
44383  *      related DropZone, it transfers the selected node.
44384  */
44385     onDblClick : function(e){
44386         var item = this.findItemFromChild(e.getTarget());
44387         if(item){
44388             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44389                 return false;
44390             }
44391             if (this.dragGroup) {
44392                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44393                     while (targets.indexOf(this.dropZone) > -1) {
44394                             targets.remove(this.dropZone);
44395                                 }
44396                     if (targets.length == 1) {
44397                                         this.dragZone.cachedTarget = null;
44398                         var el = Roo.get(targets[0].getEl());
44399                         var box = el.getBox(true);
44400                         targets[0].onNodeDrop(el.dom, {
44401                                 target: el.dom,
44402                                 xy: [box.x, box.y + box.height - 1]
44403                         }, null, this.getDragData(e));
44404                     }
44405                 }
44406         }
44407     },
44408     
44409     handleSelection: function(e) {
44410                 this.dragZone.cachedTarget = null;
44411         var item = this.findItemFromChild(e.getTarget());
44412         if (!item) {
44413                 this.clearSelections(true);
44414                 return;
44415         }
44416                 if (item && (this.multiSelect || this.singleSelect)){
44417                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44418                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44419                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44420                                 this.unselect(item);
44421                         } else {
44422                                 this.select(item, this.multiSelect && e.ctrlKey);
44423                                 this.lastSelection = item;
44424                         }
44425                 }
44426     },
44427
44428     onItemClick : function(item, index, e){
44429                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44430                         return false;
44431                 }
44432                 return true;
44433     },
44434
44435     unselect : function(nodeInfo, suppressEvent){
44436                 var node = this.getNode(nodeInfo);
44437                 if(node && this.isSelected(node)){
44438                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44439                                 Roo.fly(node).removeClass(this.selectedClass);
44440                                 this.selections.remove(node);
44441                                 if(!suppressEvent){
44442                                         this.fireEvent("selectionchange", this, this.selections);
44443                                 }
44444                         }
44445                 }
44446     }
44447 });
44448 /*
44449  * Based on:
44450  * Ext JS Library 1.1.1
44451  * Copyright(c) 2006-2007, Ext JS, LLC.
44452  *
44453  * Originally Released Under LGPL - original licence link has changed is not relivant.
44454  *
44455  * Fork - LGPL
44456  * <script type="text/javascript">
44457  */
44458  
44459 /**
44460  * @class Roo.LayoutManager
44461  * @extends Roo.util.Observable
44462  * Base class for layout managers.
44463  */
44464 Roo.LayoutManager = function(container, config){
44465     Roo.LayoutManager.superclass.constructor.call(this);
44466     this.el = Roo.get(container);
44467     // ie scrollbar fix
44468     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44469         document.body.scroll = "no";
44470     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44471         this.el.position('relative');
44472     }
44473     this.id = this.el.id;
44474     this.el.addClass("x-layout-container");
44475     /** false to disable window resize monitoring @type Boolean */
44476     this.monitorWindowResize = true;
44477     this.regions = {};
44478     this.addEvents({
44479         /**
44480          * @event layout
44481          * Fires when a layout is performed. 
44482          * @param {Roo.LayoutManager} this
44483          */
44484         "layout" : true,
44485         /**
44486          * @event regionresized
44487          * Fires when the user resizes a region. 
44488          * @param {Roo.LayoutRegion} region The resized region
44489          * @param {Number} newSize The new size (width for east/west, height for north/south)
44490          */
44491         "regionresized" : true,
44492         /**
44493          * @event regioncollapsed
44494          * Fires when a region is collapsed. 
44495          * @param {Roo.LayoutRegion} region The collapsed region
44496          */
44497         "regioncollapsed" : true,
44498         /**
44499          * @event regionexpanded
44500          * Fires when a region is expanded.  
44501          * @param {Roo.LayoutRegion} region The expanded region
44502          */
44503         "regionexpanded" : true
44504     });
44505     this.updating = false;
44506     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44507 };
44508
44509 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44510     /**
44511      * Returns true if this layout is currently being updated
44512      * @return {Boolean}
44513      */
44514     isUpdating : function(){
44515         return this.updating; 
44516     },
44517     
44518     /**
44519      * Suspend the LayoutManager from doing auto-layouts while
44520      * making multiple add or remove calls
44521      */
44522     beginUpdate : function(){
44523         this.updating = true;    
44524     },
44525     
44526     /**
44527      * Restore auto-layouts and optionally disable the manager from performing a layout
44528      * @param {Boolean} noLayout true to disable a layout update 
44529      */
44530     endUpdate : function(noLayout){
44531         this.updating = false;
44532         if(!noLayout){
44533             this.layout();
44534         }    
44535     },
44536     
44537     layout: function(){
44538         
44539     },
44540     
44541     onRegionResized : function(region, newSize){
44542         this.fireEvent("regionresized", region, newSize);
44543         this.layout();
44544     },
44545     
44546     onRegionCollapsed : function(region){
44547         this.fireEvent("regioncollapsed", region);
44548     },
44549     
44550     onRegionExpanded : function(region){
44551         this.fireEvent("regionexpanded", region);
44552     },
44553         
44554     /**
44555      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44556      * performs box-model adjustments.
44557      * @return {Object} The size as an object {width: (the width), height: (the height)}
44558      */
44559     getViewSize : function(){
44560         var size;
44561         if(this.el.dom != document.body){
44562             size = this.el.getSize();
44563         }else{
44564             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44565         }
44566         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44567         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44568         return size;
44569     },
44570     
44571     /**
44572      * Returns the Element this layout is bound to.
44573      * @return {Roo.Element}
44574      */
44575     getEl : function(){
44576         return this.el;
44577     },
44578     
44579     /**
44580      * Returns the specified region.
44581      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44582      * @return {Roo.LayoutRegion}
44583      */
44584     getRegion : function(target){
44585         return this.regions[target.toLowerCase()];
44586     },
44587     
44588     onWindowResize : function(){
44589         if(this.monitorWindowResize){
44590             this.layout();
44591         }
44592     }
44593 });/*
44594  * Based on:
44595  * Ext JS Library 1.1.1
44596  * Copyright(c) 2006-2007, Ext JS, LLC.
44597  *
44598  * Originally Released Under LGPL - original licence link has changed is not relivant.
44599  *
44600  * Fork - LGPL
44601  * <script type="text/javascript">
44602  */
44603 /**
44604  * @class Roo.BorderLayout
44605  * @extends Roo.LayoutManager
44606  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44607  * please see: <br><br>
44608  * <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>
44609  * <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>
44610  * Example:
44611  <pre><code>
44612  var layout = new Roo.BorderLayout(document.body, {
44613     north: {
44614         initialSize: 25,
44615         titlebar: false
44616     },
44617     west: {
44618         split:true,
44619         initialSize: 200,
44620         minSize: 175,
44621         maxSize: 400,
44622         titlebar: true,
44623         collapsible: true
44624     },
44625     east: {
44626         split:true,
44627         initialSize: 202,
44628         minSize: 175,
44629         maxSize: 400,
44630         titlebar: true,
44631         collapsible: true
44632     },
44633     south: {
44634         split:true,
44635         initialSize: 100,
44636         minSize: 100,
44637         maxSize: 200,
44638         titlebar: true,
44639         collapsible: true
44640     },
44641     center: {
44642         titlebar: true,
44643         autoScroll:true,
44644         resizeTabs: true,
44645         minTabWidth: 50,
44646         preferredTabWidth: 150
44647     }
44648 });
44649
44650 // shorthand
44651 var CP = Roo.ContentPanel;
44652
44653 layout.beginUpdate();
44654 layout.add("north", new CP("north", "North"));
44655 layout.add("south", new CP("south", {title: "South", closable: true}));
44656 layout.add("west", new CP("west", {title: "West"}));
44657 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44658 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44659 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44660 layout.getRegion("center").showPanel("center1");
44661 layout.endUpdate();
44662 </code></pre>
44663
44664 <b>The container the layout is rendered into can be either the body element or any other element.
44665 If it is not the body element, the container needs to either be an absolute positioned element,
44666 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44667 the container size if it is not the body element.</b>
44668
44669 * @constructor
44670 * Create a new BorderLayout
44671 * @param {String/HTMLElement/Element} container The container this layout is bound to
44672 * @param {Object} config Configuration options
44673  */
44674 Roo.BorderLayout = function(container, config){
44675     config = config || {};
44676     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44677     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44678     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44679         var target = this.factory.validRegions[i];
44680         if(config[target]){
44681             this.addRegion(target, config[target]);
44682         }
44683     }
44684 };
44685
44686 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44687     /**
44688      * Creates and adds a new region if it doesn't already exist.
44689      * @param {String} target The target region key (north, south, east, west or center).
44690      * @param {Object} config The regions config object
44691      * @return {BorderLayoutRegion} The new region
44692      */
44693     addRegion : function(target, config){
44694         if(!this.regions[target]){
44695             var r = this.factory.create(target, this, config);
44696             this.bindRegion(target, r);
44697         }
44698         return this.regions[target];
44699     },
44700
44701     // private (kinda)
44702     bindRegion : function(name, r){
44703         this.regions[name] = r;
44704         r.on("visibilitychange", this.layout, this);
44705         r.on("paneladded", this.layout, this);
44706         r.on("panelremoved", this.layout, this);
44707         r.on("invalidated", this.layout, this);
44708         r.on("resized", this.onRegionResized, this);
44709         r.on("collapsed", this.onRegionCollapsed, this);
44710         r.on("expanded", this.onRegionExpanded, this);
44711     },
44712
44713     /**
44714      * Performs a layout update.
44715      */
44716     layout : function(){
44717         if(this.updating) return;
44718         var size = this.getViewSize();
44719         var w = size.width;
44720         var h = size.height;
44721         var centerW = w;
44722         var centerH = h;
44723         var centerY = 0;
44724         var centerX = 0;
44725         //var x = 0, y = 0;
44726
44727         var rs = this.regions;
44728         var north = rs["north"];
44729         var south = rs["south"]; 
44730         var west = rs["west"];
44731         var east = rs["east"];
44732         var center = rs["center"];
44733         //if(this.hideOnLayout){ // not supported anymore
44734             //c.el.setStyle("display", "none");
44735         //}
44736         if(north && north.isVisible()){
44737             var b = north.getBox();
44738             var m = north.getMargins();
44739             b.width = w - (m.left+m.right);
44740             b.x = m.left;
44741             b.y = m.top;
44742             centerY = b.height + b.y + m.bottom;
44743             centerH -= centerY;
44744             north.updateBox(this.safeBox(b));
44745         }
44746         if(south && south.isVisible()){
44747             var b = south.getBox();
44748             var m = south.getMargins();
44749             b.width = w - (m.left+m.right);
44750             b.x = m.left;
44751             var totalHeight = (b.height + m.top + m.bottom);
44752             b.y = h - totalHeight + m.top;
44753             centerH -= totalHeight;
44754             south.updateBox(this.safeBox(b));
44755         }
44756         if(west && west.isVisible()){
44757             var b = west.getBox();
44758             var m = west.getMargins();
44759             b.height = centerH - (m.top+m.bottom);
44760             b.x = m.left;
44761             b.y = centerY + m.top;
44762             var totalWidth = (b.width + m.left + m.right);
44763             centerX += totalWidth;
44764             centerW -= totalWidth;
44765             west.updateBox(this.safeBox(b));
44766         }
44767         if(east && east.isVisible()){
44768             var b = east.getBox();
44769             var m = east.getMargins();
44770             b.height = centerH - (m.top+m.bottom);
44771             var totalWidth = (b.width + m.left + m.right);
44772             b.x = w - totalWidth + m.left;
44773             b.y = centerY + m.top;
44774             centerW -= totalWidth;
44775             east.updateBox(this.safeBox(b));
44776         }
44777         if(center){
44778             var m = center.getMargins();
44779             var centerBox = {
44780                 x: centerX + m.left,
44781                 y: centerY + m.top,
44782                 width: centerW - (m.left+m.right),
44783                 height: centerH - (m.top+m.bottom)
44784             };
44785             //if(this.hideOnLayout){
44786                 //center.el.setStyle("display", "block");
44787             //}
44788             center.updateBox(this.safeBox(centerBox));
44789         }
44790         this.el.repaint();
44791         this.fireEvent("layout", this);
44792     },
44793
44794     // private
44795     safeBox : function(box){
44796         box.width = Math.max(0, box.width);
44797         box.height = Math.max(0, box.height);
44798         return box;
44799     },
44800
44801     /**
44802      * Adds a ContentPanel (or subclass) to this layout.
44803      * @param {String} target The target region key (north, south, east, west or center).
44804      * @param {Roo.ContentPanel} panel The panel to add
44805      * @return {Roo.ContentPanel} The added panel
44806      */
44807     add : function(target, panel){
44808          
44809         target = target.toLowerCase();
44810         return this.regions[target].add(panel);
44811     },
44812
44813     /**
44814      * Remove a ContentPanel (or subclass) to this layout.
44815      * @param {String} target The target region key (north, south, east, west or center).
44816      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44817      * @return {Roo.ContentPanel} The removed panel
44818      */
44819     remove : function(target, panel){
44820         target = target.toLowerCase();
44821         return this.regions[target].remove(panel);
44822     },
44823
44824     /**
44825      * Searches all regions for a panel with the specified id
44826      * @param {String} panelId
44827      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44828      */
44829     findPanel : function(panelId){
44830         var rs = this.regions;
44831         for(var target in rs){
44832             if(typeof rs[target] != "function"){
44833                 var p = rs[target].getPanel(panelId);
44834                 if(p){
44835                     return p;
44836                 }
44837             }
44838         }
44839         return null;
44840     },
44841
44842     /**
44843      * Searches all regions for a panel with the specified id and activates (shows) it.
44844      * @param {String/ContentPanel} panelId The panels id or the panel itself
44845      * @return {Roo.ContentPanel} The shown panel or null
44846      */
44847     showPanel : function(panelId) {
44848       var rs = this.regions;
44849       for(var target in rs){
44850          var r = rs[target];
44851          if(typeof r != "function"){
44852             if(r.hasPanel(panelId)){
44853                return r.showPanel(panelId);
44854             }
44855          }
44856       }
44857       return null;
44858    },
44859
44860    /**
44861      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44862      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44863      */
44864     restoreState : function(provider){
44865         if(!provider){
44866             provider = Roo.state.Manager;
44867         }
44868         var sm = new Roo.LayoutStateManager();
44869         sm.init(this, provider);
44870     },
44871
44872     /**
44873      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44874      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44875      * a valid ContentPanel config object.  Example:
44876      * <pre><code>
44877 // Create the main layout
44878 var layout = new Roo.BorderLayout('main-ct', {
44879     west: {
44880         split:true,
44881         minSize: 175,
44882         titlebar: true
44883     },
44884     center: {
44885         title:'Components'
44886     }
44887 }, 'main-ct');
44888
44889 // Create and add multiple ContentPanels at once via configs
44890 layout.batchAdd({
44891    west: {
44892        id: 'source-files',
44893        autoCreate:true,
44894        title:'Ext Source Files',
44895        autoScroll:true,
44896        fitToFrame:true
44897    },
44898    center : {
44899        el: cview,
44900        autoScroll:true,
44901        fitToFrame:true,
44902        toolbar: tb,
44903        resizeEl:'cbody'
44904    }
44905 });
44906 </code></pre>
44907      * @param {Object} regions An object containing ContentPanel configs by region name
44908      */
44909     batchAdd : function(regions){
44910         this.beginUpdate();
44911         for(var rname in regions){
44912             var lr = this.regions[rname];
44913             if(lr){
44914                 this.addTypedPanels(lr, regions[rname]);
44915             }
44916         }
44917         this.endUpdate();
44918     },
44919
44920     // private
44921     addTypedPanels : function(lr, ps){
44922         if(typeof ps == 'string'){
44923             lr.add(new Roo.ContentPanel(ps));
44924         }
44925         else if(ps instanceof Array){
44926             for(var i =0, len = ps.length; i < len; i++){
44927                 this.addTypedPanels(lr, ps[i]);
44928             }
44929         }
44930         else if(!ps.events){ // raw config?
44931             var el = ps.el;
44932             delete ps.el; // prevent conflict
44933             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44934         }
44935         else {  // panel object assumed!
44936             lr.add(ps);
44937         }
44938     },
44939     /**
44940      * Adds a xtype elements to the layout.
44941      * <pre><code>
44942
44943 layout.addxtype({
44944        xtype : 'ContentPanel',
44945        region: 'west',
44946        items: [ .... ]
44947    }
44948 );
44949
44950 layout.addxtype({
44951         xtype : 'NestedLayoutPanel',
44952         region: 'west',
44953         layout: {
44954            center: { },
44955            west: { }   
44956         },
44957         items : [ ... list of content panels or nested layout panels.. ]
44958    }
44959 );
44960 </code></pre>
44961      * @param {Object} cfg Xtype definition of item to add.
44962      */
44963     addxtype : function(cfg)
44964     {
44965         // basically accepts a pannel...
44966         // can accept a layout region..!?!?
44967         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44968         
44969         if (!cfg.xtype.match(/Panel$/)) {
44970             return false;
44971         }
44972         var ret = false;
44973         
44974         if (typeof(cfg.region) == 'undefined') {
44975             Roo.log("Failed to add Panel, region was not set");
44976             Roo.log(cfg);
44977             return false;
44978         }
44979         var region = cfg.region;
44980         delete cfg.region;
44981         
44982           
44983         var xitems = [];
44984         if (cfg.items) {
44985             xitems = cfg.items;
44986             delete cfg.items;
44987         }
44988         var nb = false;
44989         
44990         switch(cfg.xtype) 
44991         {
44992             case 'ContentPanel':  // ContentPanel (el, cfg)
44993             case 'ScrollPanel':  // ContentPanel (el, cfg)
44994                 if(cfg.autoCreate) {
44995                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44996                 } else {
44997                     var el = this.el.createChild();
44998                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44999                 }
45000                 
45001                 this.add(region, ret);
45002                 break;
45003             
45004             
45005             case 'TreePanel': // our new panel!
45006                 cfg.el = this.el.createChild();
45007                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
45008                 this.add(region, ret);
45009                 break;
45010             
45011             case 'NestedLayoutPanel': 
45012                 // create a new Layout (which is  a Border Layout...
45013                 var el = this.el.createChild();
45014                 var clayout = cfg.layout;
45015                 delete cfg.layout;
45016                 clayout.items   = clayout.items  || [];
45017                 // replace this exitems with the clayout ones..
45018                 xitems = clayout.items;
45019                  
45020                 
45021                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
45022                     cfg.background = false;
45023                 }
45024                 var layout = new Roo.BorderLayout(el, clayout);
45025                 
45026                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
45027                 //console.log('adding nested layout panel '  + cfg.toSource());
45028                 this.add(region, ret);
45029                 nb = {}; /// find first...
45030                 break;
45031                 
45032             case 'GridPanel': 
45033             
45034                 // needs grid and region
45035                 
45036                 //var el = this.getRegion(region).el.createChild();
45037                 var el = this.el.createChild();
45038                 // create the grid first...
45039                 
45040                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
45041                 delete cfg.grid;
45042                 if (region == 'center' && this.active ) {
45043                     cfg.background = false;
45044                 }
45045                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
45046                 
45047                 this.add(region, ret);
45048                 if (cfg.background) {
45049                     ret.on('activate', function(gp) {
45050                         if (!gp.grid.rendered) {
45051                             gp.grid.render();
45052                         }
45053                     });
45054                 } else {
45055                     grid.render();
45056                 }
45057                 break;
45058            
45059                
45060                 
45061                 
45062             default: 
45063                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
45064                 return null;
45065              // GridPanel (grid, cfg)
45066             
45067         }
45068         this.beginUpdate();
45069         // add children..
45070         var region = '';
45071         var abn = {};
45072         Roo.each(xitems, function(i)  {
45073             region = nb && i.region ? i.region : false;
45074             
45075             var add = ret.addxtype(i);
45076            
45077             if (region) {
45078                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
45079                 if (!i.background) {
45080                     abn[region] = nb[region] ;
45081                 }
45082             }
45083             
45084         });
45085         this.endUpdate();
45086
45087         // make the last non-background panel active..
45088         //if (nb) { Roo.log(abn); }
45089         if (nb) {
45090             
45091             for(var r in abn) {
45092                 region = this.getRegion(r);
45093                 if (region) {
45094                     // tried using nb[r], but it does not work..
45095                      
45096                     region.showPanel(abn[r]);
45097                    
45098                 }
45099             }
45100         }
45101         return ret;
45102         
45103     }
45104 });
45105
45106 /**
45107  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
45108  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
45109  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
45110  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
45111  * <pre><code>
45112 // shorthand
45113 var CP = Roo.ContentPanel;
45114
45115 var layout = Roo.BorderLayout.create({
45116     north: {
45117         initialSize: 25,
45118         titlebar: false,
45119         panels: [new CP("north", "North")]
45120     },
45121     west: {
45122         split:true,
45123         initialSize: 200,
45124         minSize: 175,
45125         maxSize: 400,
45126         titlebar: true,
45127         collapsible: true,
45128         panels: [new CP("west", {title: "West"})]
45129     },
45130     east: {
45131         split:true,
45132         initialSize: 202,
45133         minSize: 175,
45134         maxSize: 400,
45135         titlebar: true,
45136         collapsible: true,
45137         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
45138     },
45139     south: {
45140         split:true,
45141         initialSize: 100,
45142         minSize: 100,
45143         maxSize: 200,
45144         titlebar: true,
45145         collapsible: true,
45146         panels: [new CP("south", {title: "South", closable: true})]
45147     },
45148     center: {
45149         titlebar: true,
45150         autoScroll:true,
45151         resizeTabs: true,
45152         minTabWidth: 50,
45153         preferredTabWidth: 150,
45154         panels: [
45155             new CP("center1", {title: "Close Me", closable: true}),
45156             new CP("center2", {title: "Center Panel", closable: false})
45157         ]
45158     }
45159 }, document.body);
45160
45161 layout.getRegion("center").showPanel("center1");
45162 </code></pre>
45163  * @param config
45164  * @param targetEl
45165  */
45166 Roo.BorderLayout.create = function(config, targetEl){
45167     var layout = new Roo.BorderLayout(targetEl || document.body, config);
45168     layout.beginUpdate();
45169     var regions = Roo.BorderLayout.RegionFactory.validRegions;
45170     for(var j = 0, jlen = regions.length; j < jlen; j++){
45171         var lr = regions[j];
45172         if(layout.regions[lr] && config[lr].panels){
45173             var r = layout.regions[lr];
45174             var ps = config[lr].panels;
45175             layout.addTypedPanels(r, ps);
45176         }
45177     }
45178     layout.endUpdate();
45179     return layout;
45180 };
45181
45182 // private
45183 Roo.BorderLayout.RegionFactory = {
45184     // private
45185     validRegions : ["north","south","east","west","center"],
45186
45187     // private
45188     create : function(target, mgr, config){
45189         target = target.toLowerCase();
45190         if(config.lightweight || config.basic){
45191             return new Roo.BasicLayoutRegion(mgr, config, target);
45192         }
45193         switch(target){
45194             case "north":
45195                 return new Roo.NorthLayoutRegion(mgr, config);
45196             case "south":
45197                 return new Roo.SouthLayoutRegion(mgr, config);
45198             case "east":
45199                 return new Roo.EastLayoutRegion(mgr, config);
45200             case "west":
45201                 return new Roo.WestLayoutRegion(mgr, config);
45202             case "center":
45203                 return new Roo.CenterLayoutRegion(mgr, config);
45204         }
45205         throw 'Layout region "'+target+'" not supported.';
45206     }
45207 };/*
45208  * Based on:
45209  * Ext JS Library 1.1.1
45210  * Copyright(c) 2006-2007, Ext JS, LLC.
45211  *
45212  * Originally Released Under LGPL - original licence link has changed is not relivant.
45213  *
45214  * Fork - LGPL
45215  * <script type="text/javascript">
45216  */
45217  
45218 /**
45219  * @class Roo.BasicLayoutRegion
45220  * @extends Roo.util.Observable
45221  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45222  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45223  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45224  */
45225 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45226     this.mgr = mgr;
45227     this.position  = pos;
45228     this.events = {
45229         /**
45230          * @scope Roo.BasicLayoutRegion
45231          */
45232         
45233         /**
45234          * @event beforeremove
45235          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45236          * @param {Roo.LayoutRegion} this
45237          * @param {Roo.ContentPanel} panel The panel
45238          * @param {Object} e The cancel event object
45239          */
45240         "beforeremove" : true,
45241         /**
45242          * @event invalidated
45243          * Fires when the layout for this region is changed.
45244          * @param {Roo.LayoutRegion} this
45245          */
45246         "invalidated" : true,
45247         /**
45248          * @event visibilitychange
45249          * Fires when this region is shown or hidden 
45250          * @param {Roo.LayoutRegion} this
45251          * @param {Boolean} visibility true or false
45252          */
45253         "visibilitychange" : true,
45254         /**
45255          * @event paneladded
45256          * Fires when a panel is added. 
45257          * @param {Roo.LayoutRegion} this
45258          * @param {Roo.ContentPanel} panel The panel
45259          */
45260         "paneladded" : true,
45261         /**
45262          * @event panelremoved
45263          * Fires when a panel is removed. 
45264          * @param {Roo.LayoutRegion} this
45265          * @param {Roo.ContentPanel} panel The panel
45266          */
45267         "panelremoved" : true,
45268         /**
45269          * @event collapsed
45270          * Fires when this region is collapsed.
45271          * @param {Roo.LayoutRegion} this
45272          */
45273         "collapsed" : true,
45274         /**
45275          * @event expanded
45276          * Fires when this region is expanded.
45277          * @param {Roo.LayoutRegion} this
45278          */
45279         "expanded" : true,
45280         /**
45281          * @event slideshow
45282          * Fires when this region is slid into view.
45283          * @param {Roo.LayoutRegion} this
45284          */
45285         "slideshow" : true,
45286         /**
45287          * @event slidehide
45288          * Fires when this region slides out of view. 
45289          * @param {Roo.LayoutRegion} this
45290          */
45291         "slidehide" : true,
45292         /**
45293          * @event panelactivated
45294          * Fires when a panel is activated. 
45295          * @param {Roo.LayoutRegion} this
45296          * @param {Roo.ContentPanel} panel The activated panel
45297          */
45298         "panelactivated" : true,
45299         /**
45300          * @event resized
45301          * Fires when the user resizes this region. 
45302          * @param {Roo.LayoutRegion} this
45303          * @param {Number} newSize The new size (width for east/west, height for north/south)
45304          */
45305         "resized" : true
45306     };
45307     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45308     this.panels = new Roo.util.MixedCollection();
45309     this.panels.getKey = this.getPanelId.createDelegate(this);
45310     this.box = null;
45311     this.activePanel = null;
45312     // ensure listeners are added...
45313     
45314     if (config.listeners || config.events) {
45315         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45316             listeners : config.listeners || {},
45317             events : config.events || {}
45318         });
45319     }
45320     
45321     if(skipConfig !== true){
45322         this.applyConfig(config);
45323     }
45324 };
45325
45326 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45327     getPanelId : function(p){
45328         return p.getId();
45329     },
45330     
45331     applyConfig : function(config){
45332         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45333         this.config = config;
45334         
45335     },
45336     
45337     /**
45338      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45339      * the width, for horizontal (north, south) the height.
45340      * @param {Number} newSize The new width or height
45341      */
45342     resizeTo : function(newSize){
45343         var el = this.el ? this.el :
45344                  (this.activePanel ? this.activePanel.getEl() : null);
45345         if(el){
45346             switch(this.position){
45347                 case "east":
45348                 case "west":
45349                     el.setWidth(newSize);
45350                     this.fireEvent("resized", this, newSize);
45351                 break;
45352                 case "north":
45353                 case "south":
45354                     el.setHeight(newSize);
45355                     this.fireEvent("resized", this, newSize);
45356                 break;                
45357             }
45358         }
45359     },
45360     
45361     getBox : function(){
45362         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45363     },
45364     
45365     getMargins : function(){
45366         return this.margins;
45367     },
45368     
45369     updateBox : function(box){
45370         this.box = box;
45371         var el = this.activePanel.getEl();
45372         el.dom.style.left = box.x + "px";
45373         el.dom.style.top = box.y + "px";
45374         this.activePanel.setSize(box.width, box.height);
45375     },
45376     
45377     /**
45378      * Returns the container element for this region.
45379      * @return {Roo.Element}
45380      */
45381     getEl : function(){
45382         return this.activePanel;
45383     },
45384     
45385     /**
45386      * Returns true if this region is currently visible.
45387      * @return {Boolean}
45388      */
45389     isVisible : function(){
45390         return this.activePanel ? true : false;
45391     },
45392     
45393     setActivePanel : function(panel){
45394         panel = this.getPanel(panel);
45395         if(this.activePanel && this.activePanel != panel){
45396             this.activePanel.setActiveState(false);
45397             this.activePanel.getEl().setLeftTop(-10000,-10000);
45398         }
45399         this.activePanel = panel;
45400         panel.setActiveState(true);
45401         if(this.box){
45402             panel.setSize(this.box.width, this.box.height);
45403         }
45404         this.fireEvent("panelactivated", this, panel);
45405         this.fireEvent("invalidated");
45406     },
45407     
45408     /**
45409      * Show the specified panel.
45410      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45411      * @return {Roo.ContentPanel} The shown panel or null
45412      */
45413     showPanel : function(panel){
45414         if(panel = this.getPanel(panel)){
45415             this.setActivePanel(panel);
45416         }
45417         return panel;
45418     },
45419     
45420     /**
45421      * Get the active panel for this region.
45422      * @return {Roo.ContentPanel} The active panel or null
45423      */
45424     getActivePanel : function(){
45425         return this.activePanel;
45426     },
45427     
45428     /**
45429      * Add the passed ContentPanel(s)
45430      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45431      * @return {Roo.ContentPanel} The panel added (if only one was added)
45432      */
45433     add : function(panel){
45434         if(arguments.length > 1){
45435             for(var i = 0, len = arguments.length; i < len; i++) {
45436                 this.add(arguments[i]);
45437             }
45438             return null;
45439         }
45440         if(this.hasPanel(panel)){
45441             this.showPanel(panel);
45442             return panel;
45443         }
45444         var el = panel.getEl();
45445         if(el.dom.parentNode != this.mgr.el.dom){
45446             this.mgr.el.dom.appendChild(el.dom);
45447         }
45448         if(panel.setRegion){
45449             panel.setRegion(this);
45450         }
45451         this.panels.add(panel);
45452         el.setStyle("position", "absolute");
45453         if(!panel.background){
45454             this.setActivePanel(panel);
45455             if(this.config.initialSize && this.panels.getCount()==1){
45456                 this.resizeTo(this.config.initialSize);
45457             }
45458         }
45459         this.fireEvent("paneladded", this, panel);
45460         return panel;
45461     },
45462     
45463     /**
45464      * Returns true if the panel is in this region.
45465      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45466      * @return {Boolean}
45467      */
45468     hasPanel : function(panel){
45469         if(typeof panel == "object"){ // must be panel obj
45470             panel = panel.getId();
45471         }
45472         return this.getPanel(panel) ? true : false;
45473     },
45474     
45475     /**
45476      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45477      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45478      * @param {Boolean} preservePanel Overrides the config preservePanel option
45479      * @return {Roo.ContentPanel} The panel that was removed
45480      */
45481     remove : function(panel, preservePanel){
45482         panel = this.getPanel(panel);
45483         if(!panel){
45484             return null;
45485         }
45486         var e = {};
45487         this.fireEvent("beforeremove", this, panel, e);
45488         if(e.cancel === true){
45489             return null;
45490         }
45491         var panelId = panel.getId();
45492         this.panels.removeKey(panelId);
45493         return panel;
45494     },
45495     
45496     /**
45497      * Returns the panel specified or null if it's not in this region.
45498      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45499      * @return {Roo.ContentPanel}
45500      */
45501     getPanel : function(id){
45502         if(typeof id == "object"){ // must be panel obj
45503             return id;
45504         }
45505         return this.panels.get(id);
45506     },
45507     
45508     /**
45509      * Returns this regions position (north/south/east/west/center).
45510      * @return {String} 
45511      */
45512     getPosition: function(){
45513         return this.position;    
45514     }
45515 });/*
45516  * Based on:
45517  * Ext JS Library 1.1.1
45518  * Copyright(c) 2006-2007, Ext JS, LLC.
45519  *
45520  * Originally Released Under LGPL - original licence link has changed is not relivant.
45521  *
45522  * Fork - LGPL
45523  * <script type="text/javascript">
45524  */
45525  
45526 /**
45527  * @class Roo.LayoutRegion
45528  * @extends Roo.BasicLayoutRegion
45529  * This class represents a region in a layout manager.
45530  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45531  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45532  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45533  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45534  * @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})
45535  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45536  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45537  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45538  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45539  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45540  * @cfg {String}    title           The title for the region (overrides panel titles)
45541  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45542  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45543  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45544  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45545  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45546  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45547  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45548  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45549  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45550  * @cfg {Boolean}   showPin         True to show a pin button
45551  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45552  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45553  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45554  * @cfg {Number}    width           For East/West panels
45555  * @cfg {Number}    height          For North/South panels
45556  * @cfg {Boolean}   split           To show the splitter
45557  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45558  */
45559 Roo.LayoutRegion = function(mgr, config, pos){
45560     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45561     var dh = Roo.DomHelper;
45562     /** This region's container element 
45563     * @type Roo.Element */
45564     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45565     /** This region's title element 
45566     * @type Roo.Element */
45567
45568     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45569         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45570         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45571     ]}, true);
45572     this.titleEl.enableDisplayMode();
45573     /** This region's title text element 
45574     * @type HTMLElement */
45575     this.titleTextEl = this.titleEl.dom.firstChild;
45576     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45577     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45578     this.closeBtn.enableDisplayMode();
45579     this.closeBtn.on("click", this.closeClicked, this);
45580     this.closeBtn.hide();
45581
45582     this.createBody(config);
45583     this.visible = true;
45584     this.collapsed = false;
45585
45586     if(config.hideWhenEmpty){
45587         this.hide();
45588         this.on("paneladded", this.validateVisibility, this);
45589         this.on("panelremoved", this.validateVisibility, this);
45590     }
45591     this.applyConfig(config);
45592 };
45593
45594 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45595
45596     createBody : function(){
45597         /** This region's body element 
45598         * @type Roo.Element */
45599         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45600     },
45601
45602     applyConfig : function(c){
45603         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45604             var dh = Roo.DomHelper;
45605             if(c.titlebar !== false){
45606                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45607                 this.collapseBtn.on("click", this.collapse, this);
45608                 this.collapseBtn.enableDisplayMode();
45609
45610                 if(c.showPin === true || this.showPin){
45611                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45612                     this.stickBtn.enableDisplayMode();
45613                     this.stickBtn.on("click", this.expand, this);
45614                     this.stickBtn.hide();
45615                 }
45616             }
45617             /** This region's collapsed element
45618             * @type Roo.Element */
45619             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45620                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45621             ]}, true);
45622             if(c.floatable !== false){
45623                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45624                this.collapsedEl.on("click", this.collapseClick, this);
45625             }
45626
45627             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45628                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45629                    id: "message", unselectable: "on", style:{"float":"left"}});
45630                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45631              }
45632             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45633             this.expandBtn.on("click", this.expand, this);
45634         }
45635         if(this.collapseBtn){
45636             this.collapseBtn.setVisible(c.collapsible == true);
45637         }
45638         this.cmargins = c.cmargins || this.cmargins ||
45639                          (this.position == "west" || this.position == "east" ?
45640                              {top: 0, left: 2, right:2, bottom: 0} :
45641                              {top: 2, left: 0, right:0, bottom: 2});
45642         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45643         this.bottomTabs = c.tabPosition != "top";
45644         this.autoScroll = c.autoScroll || false;
45645         if(this.autoScroll){
45646             this.bodyEl.setStyle("overflow", "auto");
45647         }else{
45648             this.bodyEl.setStyle("overflow", "hidden");
45649         }
45650         //if(c.titlebar !== false){
45651             if((!c.titlebar && !c.title) || c.titlebar === false){
45652                 this.titleEl.hide();
45653             }else{
45654                 this.titleEl.show();
45655                 if(c.title){
45656                     this.titleTextEl.innerHTML = c.title;
45657                 }
45658             }
45659         //}
45660         this.duration = c.duration || .30;
45661         this.slideDuration = c.slideDuration || .45;
45662         this.config = c;
45663         if(c.collapsed){
45664             this.collapse(true);
45665         }
45666         if(c.hidden){
45667             this.hide();
45668         }
45669     },
45670     /**
45671      * Returns true if this region is currently visible.
45672      * @return {Boolean}
45673      */
45674     isVisible : function(){
45675         return this.visible;
45676     },
45677
45678     /**
45679      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45680      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45681      */
45682     setCollapsedTitle : function(title){
45683         title = title || "&#160;";
45684         if(this.collapsedTitleTextEl){
45685             this.collapsedTitleTextEl.innerHTML = title;
45686         }
45687     },
45688
45689     getBox : function(){
45690         var b;
45691         if(!this.collapsed){
45692             b = this.el.getBox(false, true);
45693         }else{
45694             b = this.collapsedEl.getBox(false, true);
45695         }
45696         return b;
45697     },
45698
45699     getMargins : function(){
45700         return this.collapsed ? this.cmargins : this.margins;
45701     },
45702
45703     highlight : function(){
45704         this.el.addClass("x-layout-panel-dragover");
45705     },
45706
45707     unhighlight : function(){
45708         this.el.removeClass("x-layout-panel-dragover");
45709     },
45710
45711     updateBox : function(box){
45712         this.box = box;
45713         if(!this.collapsed){
45714             this.el.dom.style.left = box.x + "px";
45715             this.el.dom.style.top = box.y + "px";
45716             this.updateBody(box.width, box.height);
45717         }else{
45718             this.collapsedEl.dom.style.left = box.x + "px";
45719             this.collapsedEl.dom.style.top = box.y + "px";
45720             this.collapsedEl.setSize(box.width, box.height);
45721         }
45722         if(this.tabs){
45723             this.tabs.autoSizeTabs();
45724         }
45725     },
45726
45727     updateBody : function(w, h){
45728         if(w !== null){
45729             this.el.setWidth(w);
45730             w -= this.el.getBorderWidth("rl");
45731             if(this.config.adjustments){
45732                 w += this.config.adjustments[0];
45733             }
45734         }
45735         if(h !== null){
45736             this.el.setHeight(h);
45737             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45738             h -= this.el.getBorderWidth("tb");
45739             if(this.config.adjustments){
45740                 h += this.config.adjustments[1];
45741             }
45742             this.bodyEl.setHeight(h);
45743             if(this.tabs){
45744                 h = this.tabs.syncHeight(h);
45745             }
45746         }
45747         if(this.panelSize){
45748             w = w !== null ? w : this.panelSize.width;
45749             h = h !== null ? h : this.panelSize.height;
45750         }
45751         if(this.activePanel){
45752             var el = this.activePanel.getEl();
45753             w = w !== null ? w : el.getWidth();
45754             h = h !== null ? h : el.getHeight();
45755             this.panelSize = {width: w, height: h};
45756             this.activePanel.setSize(w, h);
45757         }
45758         if(Roo.isIE && this.tabs){
45759             this.tabs.el.repaint();
45760         }
45761     },
45762
45763     /**
45764      * Returns the container element for this region.
45765      * @return {Roo.Element}
45766      */
45767     getEl : function(){
45768         return this.el;
45769     },
45770
45771     /**
45772      * Hides this region.
45773      */
45774     hide : function(){
45775         if(!this.collapsed){
45776             this.el.dom.style.left = "-2000px";
45777             this.el.hide();
45778         }else{
45779             this.collapsedEl.dom.style.left = "-2000px";
45780             this.collapsedEl.hide();
45781         }
45782         this.visible = false;
45783         this.fireEvent("visibilitychange", this, false);
45784     },
45785
45786     /**
45787      * Shows this region if it was previously hidden.
45788      */
45789     show : function(){
45790         if(!this.collapsed){
45791             this.el.show();
45792         }else{
45793             this.collapsedEl.show();
45794         }
45795         this.visible = true;
45796         this.fireEvent("visibilitychange", this, true);
45797     },
45798
45799     closeClicked : function(){
45800         if(this.activePanel){
45801             this.remove(this.activePanel);
45802         }
45803     },
45804
45805     collapseClick : function(e){
45806         if(this.isSlid){
45807            e.stopPropagation();
45808            this.slideIn();
45809         }else{
45810            e.stopPropagation();
45811            this.slideOut();
45812         }
45813     },
45814
45815     /**
45816      * Collapses this region.
45817      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45818      */
45819     collapse : function(skipAnim){
45820         if(this.collapsed) return;
45821         this.collapsed = true;
45822         if(this.split){
45823             this.split.el.hide();
45824         }
45825         if(this.config.animate && skipAnim !== true){
45826             this.fireEvent("invalidated", this);
45827             this.animateCollapse();
45828         }else{
45829             this.el.setLocation(-20000,-20000);
45830             this.el.hide();
45831             this.collapsedEl.show();
45832             this.fireEvent("collapsed", this);
45833             this.fireEvent("invalidated", this);
45834         }
45835     },
45836
45837     animateCollapse : function(){
45838         // overridden
45839     },
45840
45841     /**
45842      * Expands this region if it was previously collapsed.
45843      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45844      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45845      */
45846     expand : function(e, skipAnim){
45847         if(e) e.stopPropagation();
45848         if(!this.collapsed || this.el.hasActiveFx()) return;
45849         if(this.isSlid){
45850             this.afterSlideIn();
45851             skipAnim = true;
45852         }
45853         this.collapsed = false;
45854         if(this.config.animate && skipAnim !== true){
45855             this.animateExpand();
45856         }else{
45857             this.el.show();
45858             if(this.split){
45859                 this.split.el.show();
45860             }
45861             this.collapsedEl.setLocation(-2000,-2000);
45862             this.collapsedEl.hide();
45863             this.fireEvent("invalidated", this);
45864             this.fireEvent("expanded", this);
45865         }
45866     },
45867
45868     animateExpand : function(){
45869         // overridden
45870     },
45871
45872     initTabs : function()
45873     {
45874         this.bodyEl.setStyle("overflow", "hidden");
45875         var ts = new Roo.TabPanel(
45876                 this.bodyEl.dom,
45877                 {
45878                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45879                     disableTooltips: this.config.disableTabTips,
45880                     toolbar : this.config.toolbar
45881                 }
45882         );
45883         if(this.config.hideTabs){
45884             ts.stripWrap.setDisplayed(false);
45885         }
45886         this.tabs = ts;
45887         ts.resizeTabs = this.config.resizeTabs === true;
45888         ts.minTabWidth = this.config.minTabWidth || 40;
45889         ts.maxTabWidth = this.config.maxTabWidth || 250;
45890         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45891         ts.monitorResize = false;
45892         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45893         ts.bodyEl.addClass('x-layout-tabs-body');
45894         this.panels.each(this.initPanelAsTab, this);
45895     },
45896
45897     initPanelAsTab : function(panel){
45898         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45899                     this.config.closeOnTab && panel.isClosable());
45900         if(panel.tabTip !== undefined){
45901             ti.setTooltip(panel.tabTip);
45902         }
45903         ti.on("activate", function(){
45904               this.setActivePanel(panel);
45905         }, this);
45906         if(this.config.closeOnTab){
45907             ti.on("beforeclose", function(t, e){
45908                 e.cancel = true;
45909                 this.remove(panel);
45910             }, this);
45911         }
45912         return ti;
45913     },
45914
45915     updatePanelTitle : function(panel, title){
45916         if(this.activePanel == panel){
45917             this.updateTitle(title);
45918         }
45919         if(this.tabs){
45920             var ti = this.tabs.getTab(panel.getEl().id);
45921             ti.setText(title);
45922             if(panel.tabTip !== undefined){
45923                 ti.setTooltip(panel.tabTip);
45924             }
45925         }
45926     },
45927
45928     updateTitle : function(title){
45929         if(this.titleTextEl && !this.config.title){
45930             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45931         }
45932     },
45933
45934     setActivePanel : function(panel){
45935         panel = this.getPanel(panel);
45936         if(this.activePanel && this.activePanel != panel){
45937             this.activePanel.setActiveState(false);
45938         }
45939         this.activePanel = panel;
45940         panel.setActiveState(true);
45941         if(this.panelSize){
45942             panel.setSize(this.panelSize.width, this.panelSize.height);
45943         }
45944         if(this.closeBtn){
45945             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45946         }
45947         this.updateTitle(panel.getTitle());
45948         if(this.tabs){
45949             this.fireEvent("invalidated", this);
45950         }
45951         this.fireEvent("panelactivated", this, panel);
45952     },
45953
45954     /**
45955      * Shows the specified panel.
45956      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45957      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45958      */
45959     showPanel : function(panel){
45960         if(panel = this.getPanel(panel)){
45961             if(this.tabs){
45962                 var tab = this.tabs.getTab(panel.getEl().id);
45963                 if(tab.isHidden()){
45964                     this.tabs.unhideTab(tab.id);
45965                 }
45966                 tab.activate();
45967             }else{
45968                 this.setActivePanel(panel);
45969             }
45970         }
45971         return panel;
45972     },
45973
45974     /**
45975      * Get the active panel for this region.
45976      * @return {Roo.ContentPanel} The active panel or null
45977      */
45978     getActivePanel : function(){
45979         return this.activePanel;
45980     },
45981
45982     validateVisibility : function(){
45983         if(this.panels.getCount() < 1){
45984             this.updateTitle("&#160;");
45985             this.closeBtn.hide();
45986             this.hide();
45987         }else{
45988             if(!this.isVisible()){
45989                 this.show();
45990             }
45991         }
45992     },
45993
45994     /**
45995      * Adds the passed ContentPanel(s) to this region.
45996      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45997      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45998      */
45999     add : function(panel){
46000         if(arguments.length > 1){
46001             for(var i = 0, len = arguments.length; i < len; i++) {
46002                 this.add(arguments[i]);
46003             }
46004             return null;
46005         }
46006         if(this.hasPanel(panel)){
46007             this.showPanel(panel);
46008             return panel;
46009         }
46010         panel.setRegion(this);
46011         this.panels.add(panel);
46012         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
46013             this.bodyEl.dom.appendChild(panel.getEl().dom);
46014             if(panel.background !== true){
46015                 this.setActivePanel(panel);
46016             }
46017             this.fireEvent("paneladded", this, panel);
46018             return panel;
46019         }
46020         if(!this.tabs){
46021             this.initTabs();
46022         }else{
46023             this.initPanelAsTab(panel);
46024         }
46025         if(panel.background !== true){
46026             this.tabs.activate(panel.getEl().id);
46027         }
46028         this.fireEvent("paneladded", this, panel);
46029         return panel;
46030     },
46031
46032     /**
46033      * Hides the tab for the specified panel.
46034      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46035      */
46036     hidePanel : function(panel){
46037         if(this.tabs && (panel = this.getPanel(panel))){
46038             this.tabs.hideTab(panel.getEl().id);
46039         }
46040     },
46041
46042     /**
46043      * Unhides the tab for a previously hidden panel.
46044      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46045      */
46046     unhidePanel : function(panel){
46047         if(this.tabs && (panel = this.getPanel(panel))){
46048             this.tabs.unhideTab(panel.getEl().id);
46049         }
46050     },
46051
46052     clearPanels : function(){
46053         while(this.panels.getCount() > 0){
46054              this.remove(this.panels.first());
46055         }
46056     },
46057
46058     /**
46059      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46060      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46061      * @param {Boolean} preservePanel Overrides the config preservePanel option
46062      * @return {Roo.ContentPanel} The panel that was removed
46063      */
46064     remove : function(panel, preservePanel){
46065         panel = this.getPanel(panel);
46066         if(!panel){
46067             return null;
46068         }
46069         var e = {};
46070         this.fireEvent("beforeremove", this, panel, e);
46071         if(e.cancel === true){
46072             return null;
46073         }
46074         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
46075         var panelId = panel.getId();
46076         this.panels.removeKey(panelId);
46077         if(preservePanel){
46078             document.body.appendChild(panel.getEl().dom);
46079         }
46080         if(this.tabs){
46081             this.tabs.removeTab(panel.getEl().id);
46082         }else if (!preservePanel){
46083             this.bodyEl.dom.removeChild(panel.getEl().dom);
46084         }
46085         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
46086             var p = this.panels.first();
46087             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
46088             tempEl.appendChild(p.getEl().dom);
46089             this.bodyEl.update("");
46090             this.bodyEl.dom.appendChild(p.getEl().dom);
46091             tempEl = null;
46092             this.updateTitle(p.getTitle());
46093             this.tabs = null;
46094             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46095             this.setActivePanel(p);
46096         }
46097         panel.setRegion(null);
46098         if(this.activePanel == panel){
46099             this.activePanel = null;
46100         }
46101         if(this.config.autoDestroy !== false && preservePanel !== true){
46102             try{panel.destroy();}catch(e){}
46103         }
46104         this.fireEvent("panelremoved", this, panel);
46105         return panel;
46106     },
46107
46108     /**
46109      * Returns the TabPanel component used by this region
46110      * @return {Roo.TabPanel}
46111      */
46112     getTabs : function(){
46113         return this.tabs;
46114     },
46115
46116     createTool : function(parentEl, className){
46117         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
46118             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
46119         btn.addClassOnOver("x-layout-tools-button-over");
46120         return btn;
46121     }
46122 });/*
46123  * Based on:
46124  * Ext JS Library 1.1.1
46125  * Copyright(c) 2006-2007, Ext JS, LLC.
46126  *
46127  * Originally Released Under LGPL - original licence link has changed is not relivant.
46128  *
46129  * Fork - LGPL
46130  * <script type="text/javascript">
46131  */
46132  
46133
46134
46135 /**
46136  * @class Roo.SplitLayoutRegion
46137  * @extends Roo.LayoutRegion
46138  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
46139  */
46140 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
46141     this.cursor = cursor;
46142     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
46143 };
46144
46145 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
46146     splitTip : "Drag to resize.",
46147     collapsibleSplitTip : "Drag to resize. Double click to hide.",
46148     useSplitTips : false,
46149
46150     applyConfig : function(config){
46151         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
46152         if(config.split){
46153             if(!this.split){
46154                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
46155                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
46156                 /** The SplitBar for this region 
46157                 * @type Roo.SplitBar */
46158                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
46159                 this.split.on("moved", this.onSplitMove, this);
46160                 this.split.useShim = config.useShim === true;
46161                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
46162                 if(this.useSplitTips){
46163                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
46164                 }
46165                 if(config.collapsible){
46166                     this.split.el.on("dblclick", this.collapse,  this);
46167                 }
46168             }
46169             if(typeof config.minSize != "undefined"){
46170                 this.split.minSize = config.minSize;
46171             }
46172             if(typeof config.maxSize != "undefined"){
46173                 this.split.maxSize = config.maxSize;
46174             }
46175             if(config.hideWhenEmpty || config.hidden || config.collapsed){
46176                 this.hideSplitter();
46177             }
46178         }
46179     },
46180
46181     getHMaxSize : function(){
46182          var cmax = this.config.maxSize || 10000;
46183          var center = this.mgr.getRegion("center");
46184          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46185     },
46186
46187     getVMaxSize : function(){
46188          var cmax = this.config.maxSize || 10000;
46189          var center = this.mgr.getRegion("center");
46190          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46191     },
46192
46193     onSplitMove : function(split, newSize){
46194         this.fireEvent("resized", this, newSize);
46195     },
46196     
46197     /** 
46198      * Returns the {@link Roo.SplitBar} for this region.
46199      * @return {Roo.SplitBar}
46200      */
46201     getSplitBar : function(){
46202         return this.split;
46203     },
46204     
46205     hide : function(){
46206         this.hideSplitter();
46207         Roo.SplitLayoutRegion.superclass.hide.call(this);
46208     },
46209
46210     hideSplitter : function(){
46211         if(this.split){
46212             this.split.el.setLocation(-2000,-2000);
46213             this.split.el.hide();
46214         }
46215     },
46216
46217     show : function(){
46218         if(this.split){
46219             this.split.el.show();
46220         }
46221         Roo.SplitLayoutRegion.superclass.show.call(this);
46222     },
46223     
46224     beforeSlide: function(){
46225         if(Roo.isGecko){// firefox overflow auto bug workaround
46226             this.bodyEl.clip();
46227             if(this.tabs) this.tabs.bodyEl.clip();
46228             if(this.activePanel){
46229                 this.activePanel.getEl().clip();
46230                 
46231                 if(this.activePanel.beforeSlide){
46232                     this.activePanel.beforeSlide();
46233                 }
46234             }
46235         }
46236     },
46237     
46238     afterSlide : function(){
46239         if(Roo.isGecko){// firefox overflow auto bug workaround
46240             this.bodyEl.unclip();
46241             if(this.tabs) this.tabs.bodyEl.unclip();
46242             if(this.activePanel){
46243                 this.activePanel.getEl().unclip();
46244                 if(this.activePanel.afterSlide){
46245                     this.activePanel.afterSlide();
46246                 }
46247             }
46248         }
46249     },
46250
46251     initAutoHide : function(){
46252         if(this.autoHide !== false){
46253             if(!this.autoHideHd){
46254                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46255                 this.autoHideHd = {
46256                     "mouseout": function(e){
46257                         if(!e.within(this.el, true)){
46258                             st.delay(500);
46259                         }
46260                     },
46261                     "mouseover" : function(e){
46262                         st.cancel();
46263                     },
46264                     scope : this
46265                 };
46266             }
46267             this.el.on(this.autoHideHd);
46268         }
46269     },
46270
46271     clearAutoHide : function(){
46272         if(this.autoHide !== false){
46273             this.el.un("mouseout", this.autoHideHd.mouseout);
46274             this.el.un("mouseover", this.autoHideHd.mouseover);
46275         }
46276     },
46277
46278     clearMonitor : function(){
46279         Roo.get(document).un("click", this.slideInIf, this);
46280     },
46281
46282     // these names are backwards but not changed for compat
46283     slideOut : function(){
46284         if(this.isSlid || this.el.hasActiveFx()){
46285             return;
46286         }
46287         this.isSlid = true;
46288         if(this.collapseBtn){
46289             this.collapseBtn.hide();
46290         }
46291         this.closeBtnState = this.closeBtn.getStyle('display');
46292         this.closeBtn.hide();
46293         if(this.stickBtn){
46294             this.stickBtn.show();
46295         }
46296         this.el.show();
46297         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46298         this.beforeSlide();
46299         this.el.setStyle("z-index", 10001);
46300         this.el.slideIn(this.getSlideAnchor(), {
46301             callback: function(){
46302                 this.afterSlide();
46303                 this.initAutoHide();
46304                 Roo.get(document).on("click", this.slideInIf, this);
46305                 this.fireEvent("slideshow", this);
46306             },
46307             scope: this,
46308             block: true
46309         });
46310     },
46311
46312     afterSlideIn : function(){
46313         this.clearAutoHide();
46314         this.isSlid = false;
46315         this.clearMonitor();
46316         this.el.setStyle("z-index", "");
46317         if(this.collapseBtn){
46318             this.collapseBtn.show();
46319         }
46320         this.closeBtn.setStyle('display', this.closeBtnState);
46321         if(this.stickBtn){
46322             this.stickBtn.hide();
46323         }
46324         this.fireEvent("slidehide", this);
46325     },
46326
46327     slideIn : function(cb){
46328         if(!this.isSlid || this.el.hasActiveFx()){
46329             Roo.callback(cb);
46330             return;
46331         }
46332         this.isSlid = false;
46333         this.beforeSlide();
46334         this.el.slideOut(this.getSlideAnchor(), {
46335             callback: function(){
46336                 this.el.setLeftTop(-10000, -10000);
46337                 this.afterSlide();
46338                 this.afterSlideIn();
46339                 Roo.callback(cb);
46340             },
46341             scope: this,
46342             block: true
46343         });
46344     },
46345     
46346     slideInIf : function(e){
46347         if(!e.within(this.el)){
46348             this.slideIn();
46349         }
46350     },
46351
46352     animateCollapse : function(){
46353         this.beforeSlide();
46354         this.el.setStyle("z-index", 20000);
46355         var anchor = this.getSlideAnchor();
46356         this.el.slideOut(anchor, {
46357             callback : function(){
46358                 this.el.setStyle("z-index", "");
46359                 this.collapsedEl.slideIn(anchor, {duration:.3});
46360                 this.afterSlide();
46361                 this.el.setLocation(-10000,-10000);
46362                 this.el.hide();
46363                 this.fireEvent("collapsed", this);
46364             },
46365             scope: this,
46366             block: true
46367         });
46368     },
46369
46370     animateExpand : function(){
46371         this.beforeSlide();
46372         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46373         this.el.setStyle("z-index", 20000);
46374         this.collapsedEl.hide({
46375             duration:.1
46376         });
46377         this.el.slideIn(this.getSlideAnchor(), {
46378             callback : function(){
46379                 this.el.setStyle("z-index", "");
46380                 this.afterSlide();
46381                 if(this.split){
46382                     this.split.el.show();
46383                 }
46384                 this.fireEvent("invalidated", this);
46385                 this.fireEvent("expanded", this);
46386             },
46387             scope: this,
46388             block: true
46389         });
46390     },
46391
46392     anchors : {
46393         "west" : "left",
46394         "east" : "right",
46395         "north" : "top",
46396         "south" : "bottom"
46397     },
46398
46399     sanchors : {
46400         "west" : "l",
46401         "east" : "r",
46402         "north" : "t",
46403         "south" : "b"
46404     },
46405
46406     canchors : {
46407         "west" : "tl-tr",
46408         "east" : "tr-tl",
46409         "north" : "tl-bl",
46410         "south" : "bl-tl"
46411     },
46412
46413     getAnchor : function(){
46414         return this.anchors[this.position];
46415     },
46416
46417     getCollapseAnchor : function(){
46418         return this.canchors[this.position];
46419     },
46420
46421     getSlideAnchor : function(){
46422         return this.sanchors[this.position];
46423     },
46424
46425     getAlignAdj : function(){
46426         var cm = this.cmargins;
46427         switch(this.position){
46428             case "west":
46429                 return [0, 0];
46430             break;
46431             case "east":
46432                 return [0, 0];
46433             break;
46434             case "north":
46435                 return [0, 0];
46436             break;
46437             case "south":
46438                 return [0, 0];
46439             break;
46440         }
46441     },
46442
46443     getExpandAdj : function(){
46444         var c = this.collapsedEl, cm = this.cmargins;
46445         switch(this.position){
46446             case "west":
46447                 return [-(cm.right+c.getWidth()+cm.left), 0];
46448             break;
46449             case "east":
46450                 return [cm.right+c.getWidth()+cm.left, 0];
46451             break;
46452             case "north":
46453                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46454             break;
46455             case "south":
46456                 return [0, cm.top+cm.bottom+c.getHeight()];
46457             break;
46458         }
46459     }
46460 });/*
46461  * Based on:
46462  * Ext JS Library 1.1.1
46463  * Copyright(c) 2006-2007, Ext JS, LLC.
46464  *
46465  * Originally Released Under LGPL - original licence link has changed is not relivant.
46466  *
46467  * Fork - LGPL
46468  * <script type="text/javascript">
46469  */
46470 /*
46471  * These classes are private internal classes
46472  */
46473 Roo.CenterLayoutRegion = function(mgr, config){
46474     Roo.LayoutRegion.call(this, mgr, config, "center");
46475     this.visible = true;
46476     this.minWidth = config.minWidth || 20;
46477     this.minHeight = config.minHeight || 20;
46478 };
46479
46480 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46481     hide : function(){
46482         // center panel can't be hidden
46483     },
46484     
46485     show : function(){
46486         // center panel can't be hidden
46487     },
46488     
46489     getMinWidth: function(){
46490         return this.minWidth;
46491     },
46492     
46493     getMinHeight: function(){
46494         return this.minHeight;
46495     }
46496 });
46497
46498
46499 Roo.NorthLayoutRegion = function(mgr, config){
46500     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46501     if(this.split){
46502         this.split.placement = Roo.SplitBar.TOP;
46503         this.split.orientation = Roo.SplitBar.VERTICAL;
46504         this.split.el.addClass("x-layout-split-v");
46505     }
46506     var size = config.initialSize || config.height;
46507     if(typeof size != "undefined"){
46508         this.el.setHeight(size);
46509     }
46510 };
46511 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46512     orientation: Roo.SplitBar.VERTICAL,
46513     getBox : function(){
46514         if(this.collapsed){
46515             return this.collapsedEl.getBox();
46516         }
46517         var box = this.el.getBox();
46518         if(this.split){
46519             box.height += this.split.el.getHeight();
46520         }
46521         return box;
46522     },
46523     
46524     updateBox : function(box){
46525         if(this.split && !this.collapsed){
46526             box.height -= this.split.el.getHeight();
46527             this.split.el.setLeft(box.x);
46528             this.split.el.setTop(box.y+box.height);
46529             this.split.el.setWidth(box.width);
46530         }
46531         if(this.collapsed){
46532             this.updateBody(box.width, null);
46533         }
46534         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46535     }
46536 });
46537
46538 Roo.SouthLayoutRegion = function(mgr, config){
46539     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46540     if(this.split){
46541         this.split.placement = Roo.SplitBar.BOTTOM;
46542         this.split.orientation = Roo.SplitBar.VERTICAL;
46543         this.split.el.addClass("x-layout-split-v");
46544     }
46545     var size = config.initialSize || config.height;
46546     if(typeof size != "undefined"){
46547         this.el.setHeight(size);
46548     }
46549 };
46550 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46551     orientation: Roo.SplitBar.VERTICAL,
46552     getBox : function(){
46553         if(this.collapsed){
46554             return this.collapsedEl.getBox();
46555         }
46556         var box = this.el.getBox();
46557         if(this.split){
46558             var sh = this.split.el.getHeight();
46559             box.height += sh;
46560             box.y -= sh;
46561         }
46562         return box;
46563     },
46564     
46565     updateBox : function(box){
46566         if(this.split && !this.collapsed){
46567             var sh = this.split.el.getHeight();
46568             box.height -= sh;
46569             box.y += sh;
46570             this.split.el.setLeft(box.x);
46571             this.split.el.setTop(box.y-sh);
46572             this.split.el.setWidth(box.width);
46573         }
46574         if(this.collapsed){
46575             this.updateBody(box.width, null);
46576         }
46577         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46578     }
46579 });
46580
46581 Roo.EastLayoutRegion = function(mgr, config){
46582     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46583     if(this.split){
46584         this.split.placement = Roo.SplitBar.RIGHT;
46585         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46586         this.split.el.addClass("x-layout-split-h");
46587     }
46588     var size = config.initialSize || config.width;
46589     if(typeof size != "undefined"){
46590         this.el.setWidth(size);
46591     }
46592 };
46593 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46594     orientation: Roo.SplitBar.HORIZONTAL,
46595     getBox : function(){
46596         if(this.collapsed){
46597             return this.collapsedEl.getBox();
46598         }
46599         var box = this.el.getBox();
46600         if(this.split){
46601             var sw = this.split.el.getWidth();
46602             box.width += sw;
46603             box.x -= sw;
46604         }
46605         return box;
46606     },
46607
46608     updateBox : function(box){
46609         if(this.split && !this.collapsed){
46610             var sw = this.split.el.getWidth();
46611             box.width -= sw;
46612             this.split.el.setLeft(box.x);
46613             this.split.el.setTop(box.y);
46614             this.split.el.setHeight(box.height);
46615             box.x += sw;
46616         }
46617         if(this.collapsed){
46618             this.updateBody(null, box.height);
46619         }
46620         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46621     }
46622 });
46623
46624 Roo.WestLayoutRegion = function(mgr, config){
46625     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46626     if(this.split){
46627         this.split.placement = Roo.SplitBar.LEFT;
46628         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46629         this.split.el.addClass("x-layout-split-h");
46630     }
46631     var size = config.initialSize || config.width;
46632     if(typeof size != "undefined"){
46633         this.el.setWidth(size);
46634     }
46635 };
46636 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46637     orientation: Roo.SplitBar.HORIZONTAL,
46638     getBox : function(){
46639         if(this.collapsed){
46640             return this.collapsedEl.getBox();
46641         }
46642         var box = this.el.getBox();
46643         if(this.split){
46644             box.width += this.split.el.getWidth();
46645         }
46646         return box;
46647     },
46648     
46649     updateBox : function(box){
46650         if(this.split && !this.collapsed){
46651             var sw = this.split.el.getWidth();
46652             box.width -= sw;
46653             this.split.el.setLeft(box.x+box.width);
46654             this.split.el.setTop(box.y);
46655             this.split.el.setHeight(box.height);
46656         }
46657         if(this.collapsed){
46658             this.updateBody(null, box.height);
46659         }
46660         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46661     }
46662 });
46663 /*
46664  * Based on:
46665  * Ext JS Library 1.1.1
46666  * Copyright(c) 2006-2007, Ext JS, LLC.
46667  *
46668  * Originally Released Under LGPL - original licence link has changed is not relivant.
46669  *
46670  * Fork - LGPL
46671  * <script type="text/javascript">
46672  */
46673  
46674  
46675 /*
46676  * Private internal class for reading and applying state
46677  */
46678 Roo.LayoutStateManager = function(layout){
46679      // default empty state
46680      this.state = {
46681         north: {},
46682         south: {},
46683         east: {},
46684         west: {}       
46685     };
46686 };
46687
46688 Roo.LayoutStateManager.prototype = {
46689     init : function(layout, provider){
46690         this.provider = provider;
46691         var state = provider.get(layout.id+"-layout-state");
46692         if(state){
46693             var wasUpdating = layout.isUpdating();
46694             if(!wasUpdating){
46695                 layout.beginUpdate();
46696             }
46697             for(var key in state){
46698                 if(typeof state[key] != "function"){
46699                     var rstate = state[key];
46700                     var r = layout.getRegion(key);
46701                     if(r && rstate){
46702                         if(rstate.size){
46703                             r.resizeTo(rstate.size);
46704                         }
46705                         if(rstate.collapsed == true){
46706                             r.collapse(true);
46707                         }else{
46708                             r.expand(null, true);
46709                         }
46710                     }
46711                 }
46712             }
46713             if(!wasUpdating){
46714                 layout.endUpdate();
46715             }
46716             this.state = state; 
46717         }
46718         this.layout = layout;
46719         layout.on("regionresized", this.onRegionResized, this);
46720         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46721         layout.on("regionexpanded", this.onRegionExpanded, this);
46722     },
46723     
46724     storeState : function(){
46725         this.provider.set(this.layout.id+"-layout-state", this.state);
46726     },
46727     
46728     onRegionResized : function(region, newSize){
46729         this.state[region.getPosition()].size = newSize;
46730         this.storeState();
46731     },
46732     
46733     onRegionCollapsed : function(region){
46734         this.state[region.getPosition()].collapsed = true;
46735         this.storeState();
46736     },
46737     
46738     onRegionExpanded : function(region){
46739         this.state[region.getPosition()].collapsed = false;
46740         this.storeState();
46741     }
46742 };/*
46743  * Based on:
46744  * Ext JS Library 1.1.1
46745  * Copyright(c) 2006-2007, Ext JS, LLC.
46746  *
46747  * Originally Released Under LGPL - original licence link has changed is not relivant.
46748  *
46749  * Fork - LGPL
46750  * <script type="text/javascript">
46751  */
46752 /**
46753  * @class Roo.ContentPanel
46754  * @extends Roo.util.Observable
46755  * A basic ContentPanel element.
46756  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46757  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46758  * @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
46759  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46760  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46761  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46762  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46763  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46764  * @cfg {String} title          The title for this panel
46765  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46766  * @cfg {String} url            Calls {@link #setUrl} with this value
46767  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46768  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46769  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46770  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46771
46772  * @constructor
46773  * Create a new ContentPanel.
46774  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46775  * @param {String/Object} config A string to set only the title or a config object
46776  * @param {String} content (optional) Set the HTML content for this panel
46777  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46778  */
46779 Roo.ContentPanel = function(el, config, content){
46780     
46781      
46782     /*
46783     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46784         config = el;
46785         el = Roo.id();
46786     }
46787     if (config && config.parentLayout) { 
46788         el = config.parentLayout.el.createChild(); 
46789     }
46790     */
46791     if(el.autoCreate){ // xtype is available if this is called from factory
46792         config = el;
46793         el = Roo.id();
46794     }
46795     this.el = Roo.get(el);
46796     if(!this.el && config && config.autoCreate){
46797         if(typeof config.autoCreate == "object"){
46798             if(!config.autoCreate.id){
46799                 config.autoCreate.id = config.id||el;
46800             }
46801             this.el = Roo.DomHelper.append(document.body,
46802                         config.autoCreate, true);
46803         }else{
46804             this.el = Roo.DomHelper.append(document.body,
46805                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46806         }
46807     }
46808     this.closable = false;
46809     this.loaded = false;
46810     this.active = false;
46811     if(typeof config == "string"){
46812         this.title = config;
46813     }else{
46814         Roo.apply(this, config);
46815     }
46816     
46817     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46818         this.wrapEl = this.el.wrap();
46819         this.toolbar.container = this.el.insertSibling(false, 'before');
46820         this.toolbar = new Roo.Toolbar(this.toolbar);
46821     }
46822     
46823     
46824     
46825     if(this.resizeEl){
46826         this.resizeEl = Roo.get(this.resizeEl, true);
46827     }else{
46828         this.resizeEl = this.el;
46829     }
46830     this.addEvents({
46831         /**
46832          * @event activate
46833          * Fires when this panel is activated. 
46834          * @param {Roo.ContentPanel} this
46835          */
46836         "activate" : true,
46837         /**
46838          * @event deactivate
46839          * Fires when this panel is activated. 
46840          * @param {Roo.ContentPanel} this
46841          */
46842         "deactivate" : true,
46843
46844         /**
46845          * @event resize
46846          * Fires when this panel is resized if fitToFrame is true.
46847          * @param {Roo.ContentPanel} this
46848          * @param {Number} width The width after any component adjustments
46849          * @param {Number} height The height after any component adjustments
46850          */
46851         "resize" : true,
46852         
46853          /**
46854          * @event render
46855          * Fires when this tab is created
46856          * @param {Roo.ContentPanel} this
46857          */
46858         "render" : true
46859         
46860         
46861         
46862     });
46863     if(this.autoScroll){
46864         this.resizeEl.setStyle("overflow", "auto");
46865     } else {
46866         // fix randome scrolling
46867         this.el.on('scroll', function() {
46868             Roo.log('fix random scolling');
46869             this.scrollTo('top',0); 
46870         });
46871     }
46872     content = content || this.content;
46873     if(content){
46874         this.setContent(content);
46875     }
46876     if(config && config.url){
46877         this.setUrl(this.url, this.params, this.loadOnce);
46878     }
46879     
46880     
46881     
46882     Roo.ContentPanel.superclass.constructor.call(this);
46883     
46884     this.fireEvent('render', this);
46885 };
46886
46887 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46888     tabTip:'',
46889     setRegion : function(region){
46890         this.region = region;
46891         if(region){
46892            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46893         }else{
46894            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46895         } 
46896     },
46897     
46898     /**
46899      * Returns the toolbar for this Panel if one was configured. 
46900      * @return {Roo.Toolbar} 
46901      */
46902     getToolbar : function(){
46903         return this.toolbar;
46904     },
46905     
46906     setActiveState : function(active){
46907         this.active = active;
46908         if(!active){
46909             this.fireEvent("deactivate", this);
46910         }else{
46911             this.fireEvent("activate", this);
46912         }
46913     },
46914     /**
46915      * Updates this panel's element
46916      * @param {String} content The new content
46917      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46918     */
46919     setContent : function(content, loadScripts){
46920         this.el.update(content, loadScripts);
46921     },
46922
46923     ignoreResize : function(w, h){
46924         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46925             return true;
46926         }else{
46927             this.lastSize = {width: w, height: h};
46928             return false;
46929         }
46930     },
46931     /**
46932      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46933      * @return {Roo.UpdateManager} The UpdateManager
46934      */
46935     getUpdateManager : function(){
46936         return this.el.getUpdateManager();
46937     },
46938      /**
46939      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46940      * @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:
46941 <pre><code>
46942 panel.load({
46943     url: "your-url.php",
46944     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46945     callback: yourFunction,
46946     scope: yourObject, //(optional scope)
46947     discardUrl: false,
46948     nocache: false,
46949     text: "Loading...",
46950     timeout: 30,
46951     scripts: false
46952 });
46953 </code></pre>
46954      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46955      * 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.
46956      * @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}
46957      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46958      * @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.
46959      * @return {Roo.ContentPanel} this
46960      */
46961     load : function(){
46962         var um = this.el.getUpdateManager();
46963         um.update.apply(um, arguments);
46964         return this;
46965     },
46966
46967
46968     /**
46969      * 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.
46970      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46971      * @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)
46972      * @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)
46973      * @return {Roo.UpdateManager} The UpdateManager
46974      */
46975     setUrl : function(url, params, loadOnce){
46976         if(this.refreshDelegate){
46977             this.removeListener("activate", this.refreshDelegate);
46978         }
46979         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46980         this.on("activate", this.refreshDelegate);
46981         return this.el.getUpdateManager();
46982     },
46983     
46984     _handleRefresh : function(url, params, loadOnce){
46985         if(!loadOnce || !this.loaded){
46986             var updater = this.el.getUpdateManager();
46987             updater.update(url, params, this._setLoaded.createDelegate(this));
46988         }
46989     },
46990     
46991     _setLoaded : function(){
46992         this.loaded = true;
46993     }, 
46994     
46995     /**
46996      * Returns this panel's id
46997      * @return {String} 
46998      */
46999     getId : function(){
47000         return this.el.id;
47001     },
47002     
47003     /** 
47004      * Returns this panel's element - used by regiosn to add.
47005      * @return {Roo.Element} 
47006      */
47007     getEl : function(){
47008         return this.wrapEl || this.el;
47009     },
47010     
47011     adjustForComponents : function(width, height){
47012         if(this.resizeEl != this.el){
47013             width -= this.el.getFrameWidth('lr');
47014             height -= this.el.getFrameWidth('tb');
47015         }
47016         if(this.toolbar){
47017             var te = this.toolbar.getEl();
47018             height -= te.getHeight();
47019             te.setWidth(width);
47020         }
47021         if(this.adjustments){
47022             width += this.adjustments[0];
47023             height += this.adjustments[1];
47024         }
47025         return {"width": width, "height": height};
47026     },
47027     
47028     setSize : function(width, height){
47029         if(this.fitToFrame && !this.ignoreResize(width, height)){
47030             if(this.fitContainer && this.resizeEl != this.el){
47031                 this.el.setSize(width, height);
47032             }
47033             var size = this.adjustForComponents(width, height);
47034             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
47035             this.fireEvent('resize', this, size.width, size.height);
47036         }
47037     },
47038     
47039     /**
47040      * Returns this panel's title
47041      * @return {String} 
47042      */
47043     getTitle : function(){
47044         return this.title;
47045     },
47046     
47047     /**
47048      * Set this panel's title
47049      * @param {String} title
47050      */
47051     setTitle : function(title){
47052         this.title = title;
47053         if(this.region){
47054             this.region.updatePanelTitle(this, title);
47055         }
47056     },
47057     
47058     /**
47059      * Returns true is this panel was configured to be closable
47060      * @return {Boolean} 
47061      */
47062     isClosable : function(){
47063         return this.closable;
47064     },
47065     
47066     beforeSlide : function(){
47067         this.el.clip();
47068         this.resizeEl.clip();
47069     },
47070     
47071     afterSlide : function(){
47072         this.el.unclip();
47073         this.resizeEl.unclip();
47074     },
47075     
47076     /**
47077      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
47078      *   Will fail silently if the {@link #setUrl} method has not been called.
47079      *   This does not activate the panel, just updates its content.
47080      */
47081     refresh : function(){
47082         if(this.refreshDelegate){
47083            this.loaded = false;
47084            this.refreshDelegate();
47085         }
47086     },
47087     
47088     /**
47089      * Destroys this panel
47090      */
47091     destroy : function(){
47092         this.el.removeAllListeners();
47093         var tempEl = document.createElement("span");
47094         tempEl.appendChild(this.el.dom);
47095         tempEl.innerHTML = "";
47096         this.el.remove();
47097         this.el = null;
47098     },
47099     
47100     /**
47101      * form - if the content panel contains a form - this is a reference to it.
47102      * @type {Roo.form.Form}
47103      */
47104     form : false,
47105     /**
47106      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
47107      *    This contains a reference to it.
47108      * @type {Roo.View}
47109      */
47110     view : false,
47111     
47112       /**
47113      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
47114      * <pre><code>
47115
47116 layout.addxtype({
47117        xtype : 'Form',
47118        items: [ .... ]
47119    }
47120 );
47121
47122 </code></pre>
47123      * @param {Object} cfg Xtype definition of item to add.
47124      */
47125     
47126     addxtype : function(cfg) {
47127         // add form..
47128         if (cfg.xtype.match(/^Form$/)) {
47129             var el = this.el.createChild();
47130
47131             this.form = new  Roo.form.Form(cfg);
47132             
47133             
47134             if ( this.form.allItems.length) this.form.render(el.dom);
47135             return this.form;
47136         }
47137         // should only have one of theses..
47138         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
47139             // views..
47140             cfg.el = this.el.appendChild(document.createElement("div"));
47141             // factory?
47142             
47143             var ret = new Roo.factory(cfg);
47144             ret.render && ret.render(false, ''); // render blank..
47145             this.view = ret;
47146             return ret;
47147         }
47148         return false;
47149     }
47150 });
47151
47152 /**
47153  * @class Roo.GridPanel
47154  * @extends Roo.ContentPanel
47155  * @constructor
47156  * Create a new GridPanel.
47157  * @param {Roo.grid.Grid} grid The grid for this panel
47158  * @param {String/Object} config A string to set only the panel's title, or a config object
47159  */
47160 Roo.GridPanel = function(grid, config){
47161     
47162   
47163     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
47164         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
47165         
47166     this.wrapper.dom.appendChild(grid.getGridEl().dom);
47167     
47168     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
47169     
47170     if(this.toolbar){
47171         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
47172     }
47173     // xtype created footer. - not sure if will work as we normally have to render first..
47174     if (this.footer && !this.footer.el && this.footer.xtype) {
47175         
47176         this.footer.container = this.grid.getView().getFooterPanel(true);
47177         this.footer.dataSource = this.grid.dataSource;
47178         this.footer = Roo.factory(this.footer, Roo);
47179         
47180     }
47181     
47182     grid.monitorWindowResize = false; // turn off autosizing
47183     grid.autoHeight = false;
47184     grid.autoWidth = false;
47185     this.grid = grid;
47186     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47187 };
47188
47189 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47190     getId : function(){
47191         return this.grid.id;
47192     },
47193     
47194     /**
47195      * Returns the grid for this panel
47196      * @return {Roo.grid.Grid} 
47197      */
47198     getGrid : function(){
47199         return this.grid;    
47200     },
47201     
47202     setSize : function(width, height){
47203         if(!this.ignoreResize(width, height)){
47204             var grid = this.grid;
47205             var size = this.adjustForComponents(width, height);
47206             grid.getGridEl().setSize(size.width, size.height);
47207             grid.autoSize();
47208         }
47209     },
47210     
47211     beforeSlide : function(){
47212         this.grid.getView().scroller.clip();
47213     },
47214     
47215     afterSlide : function(){
47216         this.grid.getView().scroller.unclip();
47217     },
47218     
47219     destroy : function(){
47220         this.grid.destroy();
47221         delete this.grid;
47222         Roo.GridPanel.superclass.destroy.call(this); 
47223     }
47224 });
47225
47226
47227 /**
47228  * @class Roo.NestedLayoutPanel
47229  * @extends Roo.ContentPanel
47230  * @constructor
47231  * Create a new NestedLayoutPanel.
47232  * 
47233  * 
47234  * @param {Roo.BorderLayout} layout The layout for this panel
47235  * @param {String/Object} config A string to set only the title or a config object
47236  */
47237 Roo.NestedLayoutPanel = function(layout, config)
47238 {
47239     // construct with only one argument..
47240     /* FIXME - implement nicer consturctors
47241     if (layout.layout) {
47242         config = layout;
47243         layout = config.layout;
47244         delete config.layout;
47245     }
47246     if (layout.xtype && !layout.getEl) {
47247         // then layout needs constructing..
47248         layout = Roo.factory(layout, Roo);
47249     }
47250     */
47251     
47252     
47253     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47254     
47255     layout.monitorWindowResize = false; // turn off autosizing
47256     this.layout = layout;
47257     this.layout.getEl().addClass("x-layout-nested-layout");
47258     
47259     
47260     
47261     
47262 };
47263
47264 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47265
47266     setSize : function(width, height){
47267         if(!this.ignoreResize(width, height)){
47268             var size = this.adjustForComponents(width, height);
47269             var el = this.layout.getEl();
47270             el.setSize(size.width, size.height);
47271             var touch = el.dom.offsetWidth;
47272             this.layout.layout();
47273             // ie requires a double layout on the first pass
47274             if(Roo.isIE && !this.initialized){
47275                 this.initialized = true;
47276                 this.layout.layout();
47277             }
47278         }
47279     },
47280     
47281     // activate all subpanels if not currently active..
47282     
47283     setActiveState : function(active){
47284         this.active = active;
47285         if(!active){
47286             this.fireEvent("deactivate", this);
47287             return;
47288         }
47289         
47290         this.fireEvent("activate", this);
47291         // not sure if this should happen before or after..
47292         if (!this.layout) {
47293             return; // should not happen..
47294         }
47295         var reg = false;
47296         for (var r in this.layout.regions) {
47297             reg = this.layout.getRegion(r);
47298             if (reg.getActivePanel()) {
47299                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47300                 reg.setActivePanel(reg.getActivePanel());
47301                 continue;
47302             }
47303             if (!reg.panels.length) {
47304                 continue;
47305             }
47306             reg.showPanel(reg.getPanel(0));
47307         }
47308         
47309         
47310         
47311         
47312     },
47313     
47314     /**
47315      * Returns the nested BorderLayout for this panel
47316      * @return {Roo.BorderLayout} 
47317      */
47318     getLayout : function(){
47319         return this.layout;
47320     },
47321     
47322      /**
47323      * Adds a xtype elements to the layout of the nested panel
47324      * <pre><code>
47325
47326 panel.addxtype({
47327        xtype : 'ContentPanel',
47328        region: 'west',
47329        items: [ .... ]
47330    }
47331 );
47332
47333 panel.addxtype({
47334         xtype : 'NestedLayoutPanel',
47335         region: 'west',
47336         layout: {
47337            center: { },
47338            west: { }   
47339         },
47340         items : [ ... list of content panels or nested layout panels.. ]
47341    }
47342 );
47343 </code></pre>
47344      * @param {Object} cfg Xtype definition of item to add.
47345      */
47346     addxtype : function(cfg) {
47347         return this.layout.addxtype(cfg);
47348     
47349     }
47350 });
47351
47352 Roo.ScrollPanel = function(el, config, content){
47353     config = config || {};
47354     config.fitToFrame = true;
47355     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47356     
47357     this.el.dom.style.overflow = "hidden";
47358     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47359     this.el.removeClass("x-layout-inactive-content");
47360     this.el.on("mousewheel", this.onWheel, this);
47361
47362     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47363     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47364     up.unselectable(); down.unselectable();
47365     up.on("click", this.scrollUp, this);
47366     down.on("click", this.scrollDown, this);
47367     up.addClassOnOver("x-scroller-btn-over");
47368     down.addClassOnOver("x-scroller-btn-over");
47369     up.addClassOnClick("x-scroller-btn-click");
47370     down.addClassOnClick("x-scroller-btn-click");
47371     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47372
47373     this.resizeEl = this.el;
47374     this.el = wrap; this.up = up; this.down = down;
47375 };
47376
47377 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47378     increment : 100,
47379     wheelIncrement : 5,
47380     scrollUp : function(){
47381         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47382     },
47383
47384     scrollDown : function(){
47385         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47386     },
47387
47388     afterScroll : function(){
47389         var el = this.resizeEl;
47390         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47391         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47392         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47393     },
47394
47395     setSize : function(){
47396         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47397         this.afterScroll();
47398     },
47399
47400     onWheel : function(e){
47401         var d = e.getWheelDelta();
47402         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47403         this.afterScroll();
47404         e.stopEvent();
47405     },
47406
47407     setContent : function(content, loadScripts){
47408         this.resizeEl.update(content, loadScripts);
47409     }
47410
47411 });
47412
47413
47414
47415
47416
47417
47418
47419
47420
47421 /**
47422  * @class Roo.TreePanel
47423  * @extends Roo.ContentPanel
47424  * @constructor
47425  * Create a new TreePanel. - defaults to fit/scoll contents.
47426  * @param {String/Object} config A string to set only the panel's title, or a config object
47427  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47428  */
47429 Roo.TreePanel = function(config){
47430     var el = config.el;
47431     var tree = config.tree;
47432     delete config.tree; 
47433     delete config.el; // hopefull!
47434     
47435     // wrapper for IE7 strict & safari scroll issue
47436     
47437     var treeEl = el.createChild();
47438     config.resizeEl = treeEl;
47439     
47440     
47441     
47442     Roo.TreePanel.superclass.constructor.call(this, el, config);
47443  
47444  
47445     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47446     //console.log(tree);
47447     this.on('activate', function()
47448     {
47449         if (this.tree.rendered) {
47450             return;
47451         }
47452         //console.log('render tree');
47453         this.tree.render();
47454     });
47455     
47456     this.on('resize',  function (cp, w, h) {
47457             this.tree.innerCt.setWidth(w);
47458             this.tree.innerCt.setHeight(h);
47459             this.tree.innerCt.setStyle('overflow-y', 'auto');
47460     });
47461
47462         
47463     
47464 };
47465
47466 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47467     fitToFrame : true,
47468     autoScroll : true
47469 });
47470
47471
47472
47473
47474
47475
47476
47477
47478
47479
47480
47481 /*
47482  * Based on:
47483  * Ext JS Library 1.1.1
47484  * Copyright(c) 2006-2007, Ext JS, LLC.
47485  *
47486  * Originally Released Under LGPL - original licence link has changed is not relivant.
47487  *
47488  * Fork - LGPL
47489  * <script type="text/javascript">
47490  */
47491  
47492
47493 /**
47494  * @class Roo.ReaderLayout
47495  * @extends Roo.BorderLayout
47496  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47497  * center region containing two nested regions (a top one for a list view and one for item preview below),
47498  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47499  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47500  * expedites the setup of the overall layout and regions for this common application style.
47501  * Example:
47502  <pre><code>
47503 var reader = new Roo.ReaderLayout();
47504 var CP = Roo.ContentPanel;  // shortcut for adding
47505
47506 reader.beginUpdate();
47507 reader.add("north", new CP("north", "North"));
47508 reader.add("west", new CP("west", {title: "West"}));
47509 reader.add("east", new CP("east", {title: "East"}));
47510
47511 reader.regions.listView.add(new CP("listView", "List"));
47512 reader.regions.preview.add(new CP("preview", "Preview"));
47513 reader.endUpdate();
47514 </code></pre>
47515 * @constructor
47516 * Create a new ReaderLayout
47517 * @param {Object} config Configuration options
47518 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47519 * document.body if omitted)
47520 */
47521 Roo.ReaderLayout = function(config, renderTo){
47522     var c = config || {size:{}};
47523     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47524         north: c.north !== false ? Roo.apply({
47525             split:false,
47526             initialSize: 32,
47527             titlebar: false
47528         }, c.north) : false,
47529         west: c.west !== false ? Roo.apply({
47530             split:true,
47531             initialSize: 200,
47532             minSize: 175,
47533             maxSize: 400,
47534             titlebar: true,
47535             collapsible: true,
47536             animate: true,
47537             margins:{left:5,right:0,bottom:5,top:5},
47538             cmargins:{left:5,right:5,bottom:5,top:5}
47539         }, c.west) : false,
47540         east: c.east !== false ? Roo.apply({
47541             split:true,
47542             initialSize: 200,
47543             minSize: 175,
47544             maxSize: 400,
47545             titlebar: true,
47546             collapsible: true,
47547             animate: true,
47548             margins:{left:0,right:5,bottom:5,top:5},
47549             cmargins:{left:5,right:5,bottom:5,top:5}
47550         }, c.east) : false,
47551         center: Roo.apply({
47552             tabPosition: 'top',
47553             autoScroll:false,
47554             closeOnTab: true,
47555             titlebar:false,
47556             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47557         }, c.center)
47558     });
47559
47560     this.el.addClass('x-reader');
47561
47562     this.beginUpdate();
47563
47564     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47565         south: c.preview !== false ? Roo.apply({
47566             split:true,
47567             initialSize: 200,
47568             minSize: 100,
47569             autoScroll:true,
47570             collapsible:true,
47571             titlebar: true,
47572             cmargins:{top:5,left:0, right:0, bottom:0}
47573         }, c.preview) : false,
47574         center: Roo.apply({
47575             autoScroll:false,
47576             titlebar:false,
47577             minHeight:200
47578         }, c.listView)
47579     });
47580     this.add('center', new Roo.NestedLayoutPanel(inner,
47581             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47582
47583     this.endUpdate();
47584
47585     this.regions.preview = inner.getRegion('south');
47586     this.regions.listView = inner.getRegion('center');
47587 };
47588
47589 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47590  * Based on:
47591  * Ext JS Library 1.1.1
47592  * Copyright(c) 2006-2007, Ext JS, LLC.
47593  *
47594  * Originally Released Under LGPL - original licence link has changed is not relivant.
47595  *
47596  * Fork - LGPL
47597  * <script type="text/javascript">
47598  */
47599  
47600 /**
47601  * @class Roo.grid.Grid
47602  * @extends Roo.util.Observable
47603  * This class represents the primary interface of a component based grid control.
47604  * <br><br>Usage:<pre><code>
47605  var grid = new Roo.grid.Grid("my-container-id", {
47606      ds: myDataStore,
47607      cm: myColModel,
47608      selModel: mySelectionModel,
47609      autoSizeColumns: true,
47610      monitorWindowResize: false,
47611      trackMouseOver: true
47612  });
47613  // set any options
47614  grid.render();
47615  * </code></pre>
47616  * <b>Common Problems:</b><br/>
47617  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47618  * element will correct this<br/>
47619  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47620  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47621  * are unpredictable.<br/>
47622  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47623  * grid to calculate dimensions/offsets.<br/>
47624   * @constructor
47625  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47626  * The container MUST have some type of size defined for the grid to fill. The container will be
47627  * automatically set to position relative if it isn't already.
47628  * @param {Object} config A config object that sets properties on this grid.
47629  */
47630 Roo.grid.Grid = function(container, config){
47631         // initialize the container
47632         this.container = Roo.get(container);
47633         this.container.update("");
47634         this.container.setStyle("overflow", "hidden");
47635     this.container.addClass('x-grid-container');
47636
47637     this.id = this.container.id;
47638
47639     Roo.apply(this, config);
47640     // check and correct shorthanded configs
47641     if(this.ds){
47642         this.dataSource = this.ds;
47643         delete this.ds;
47644     }
47645     if(this.cm){
47646         this.colModel = this.cm;
47647         delete this.cm;
47648     }
47649     if(this.sm){
47650         this.selModel = this.sm;
47651         delete this.sm;
47652     }
47653
47654     if (this.selModel) {
47655         this.selModel = Roo.factory(this.selModel, Roo.grid);
47656         this.sm = this.selModel;
47657         this.sm.xmodule = this.xmodule || false;
47658     }
47659     if (typeof(this.colModel.config) == 'undefined') {
47660         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47661         this.cm = this.colModel;
47662         this.cm.xmodule = this.xmodule || false;
47663     }
47664     if (this.dataSource) {
47665         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47666         this.ds = this.dataSource;
47667         this.ds.xmodule = this.xmodule || false;
47668          
47669     }
47670     
47671     
47672     
47673     if(this.width){
47674         this.container.setWidth(this.width);
47675     }
47676
47677     if(this.height){
47678         this.container.setHeight(this.height);
47679     }
47680     /** @private */
47681         this.addEvents({
47682         // raw events
47683         /**
47684          * @event click
47685          * The raw click event for the entire grid.
47686          * @param {Roo.EventObject} e
47687          */
47688         "click" : true,
47689         /**
47690          * @event dblclick
47691          * The raw dblclick event for the entire grid.
47692          * @param {Roo.EventObject} e
47693          */
47694         "dblclick" : true,
47695         /**
47696          * @event contextmenu
47697          * The raw contextmenu event for the entire grid.
47698          * @param {Roo.EventObject} e
47699          */
47700         "contextmenu" : true,
47701         /**
47702          * @event mousedown
47703          * The raw mousedown event for the entire grid.
47704          * @param {Roo.EventObject} e
47705          */
47706         "mousedown" : true,
47707         /**
47708          * @event mouseup
47709          * The raw mouseup event for the entire grid.
47710          * @param {Roo.EventObject} e
47711          */
47712         "mouseup" : true,
47713         /**
47714          * @event mouseover
47715          * The raw mouseover event for the entire grid.
47716          * @param {Roo.EventObject} e
47717          */
47718         "mouseover" : true,
47719         /**
47720          * @event mouseout
47721          * The raw mouseout event for the entire grid.
47722          * @param {Roo.EventObject} e
47723          */
47724         "mouseout" : true,
47725         /**
47726          * @event keypress
47727          * The raw keypress event for the entire grid.
47728          * @param {Roo.EventObject} e
47729          */
47730         "keypress" : true,
47731         /**
47732          * @event keydown
47733          * The raw keydown event for the entire grid.
47734          * @param {Roo.EventObject} e
47735          */
47736         "keydown" : true,
47737
47738         // custom events
47739
47740         /**
47741          * @event cellclick
47742          * Fires when a cell is clicked
47743          * @param {Grid} this
47744          * @param {Number} rowIndex
47745          * @param {Number} columnIndex
47746          * @param {Roo.EventObject} e
47747          */
47748         "cellclick" : true,
47749         /**
47750          * @event celldblclick
47751          * Fires when a cell is double clicked
47752          * @param {Grid} this
47753          * @param {Number} rowIndex
47754          * @param {Number} columnIndex
47755          * @param {Roo.EventObject} e
47756          */
47757         "celldblclick" : true,
47758         /**
47759          * @event rowclick
47760          * Fires when a row is clicked
47761          * @param {Grid} this
47762          * @param {Number} rowIndex
47763          * @param {Roo.EventObject} e
47764          */
47765         "rowclick" : true,
47766         /**
47767          * @event rowdblclick
47768          * Fires when a row is double clicked
47769          * @param {Grid} this
47770          * @param {Number} rowIndex
47771          * @param {Roo.EventObject} e
47772          */
47773         "rowdblclick" : true,
47774         /**
47775          * @event headerclick
47776          * Fires when a header is clicked
47777          * @param {Grid} this
47778          * @param {Number} columnIndex
47779          * @param {Roo.EventObject} e
47780          */
47781         "headerclick" : true,
47782         /**
47783          * @event headerdblclick
47784          * Fires when a header cell is double clicked
47785          * @param {Grid} this
47786          * @param {Number} columnIndex
47787          * @param {Roo.EventObject} e
47788          */
47789         "headerdblclick" : true,
47790         /**
47791          * @event rowcontextmenu
47792          * Fires when a row is right clicked
47793          * @param {Grid} this
47794          * @param {Number} rowIndex
47795          * @param {Roo.EventObject} e
47796          */
47797         "rowcontextmenu" : true,
47798         /**
47799          * @event cellcontextmenu
47800          * Fires when a cell is right clicked
47801          * @param {Grid} this
47802          * @param {Number} rowIndex
47803          * @param {Number} cellIndex
47804          * @param {Roo.EventObject} e
47805          */
47806          "cellcontextmenu" : true,
47807         /**
47808          * @event headercontextmenu
47809          * Fires when a header is right clicked
47810          * @param {Grid} this
47811          * @param {Number} columnIndex
47812          * @param {Roo.EventObject} e
47813          */
47814         "headercontextmenu" : true,
47815         /**
47816          * @event bodyscroll
47817          * Fires when the body element is scrolled
47818          * @param {Number} scrollLeft
47819          * @param {Number} scrollTop
47820          */
47821         "bodyscroll" : true,
47822         /**
47823          * @event columnresize
47824          * Fires when the user resizes a column
47825          * @param {Number} columnIndex
47826          * @param {Number} newSize
47827          */
47828         "columnresize" : true,
47829         /**
47830          * @event columnmove
47831          * Fires when the user moves a column
47832          * @param {Number} oldIndex
47833          * @param {Number} newIndex
47834          */
47835         "columnmove" : true,
47836         /**
47837          * @event startdrag
47838          * Fires when row(s) start being dragged
47839          * @param {Grid} this
47840          * @param {Roo.GridDD} dd The drag drop object
47841          * @param {event} e The raw browser event
47842          */
47843         "startdrag" : true,
47844         /**
47845          * @event enddrag
47846          * Fires when a drag operation is complete
47847          * @param {Grid} this
47848          * @param {Roo.GridDD} dd The drag drop object
47849          * @param {event} e The raw browser event
47850          */
47851         "enddrag" : true,
47852         /**
47853          * @event dragdrop
47854          * Fires when dragged row(s) are dropped on a valid DD target
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         "dragdrop" : true,
47861         /**
47862          * @event dragover
47863          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
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         "dragover" : true,
47870         /**
47871          * @event dragenter
47872          *  Fires when the dragged row(s) first cross 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         "dragenter" : true,
47879         /**
47880          * @event dragout
47881          * Fires when the dragged row(s) leave another DD target while being dragged
47882          * @param {Grid} this
47883          * @param {Roo.GridDD} dd The drag drop object
47884          * @param {String} targetId The target drag drop object
47885          * @param {event} e The raw browser event
47886          */
47887         "dragout" : true,
47888         /**
47889          * @event rowclass
47890          * Fires when a row is rendered, so you can change add a style to it.
47891          * @param {GridView} gridview   The grid view
47892          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47893          */
47894         'rowclass' : true,
47895
47896         /**
47897          * @event render
47898          * Fires when the grid is rendered
47899          * @param {Grid} grid
47900          */
47901         'render' : true
47902     });
47903
47904     Roo.grid.Grid.superclass.constructor.call(this);
47905 };
47906 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47907     
47908     /**
47909      * @cfg {String} ddGroup - drag drop group.
47910      */
47911
47912     /**
47913      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47914      */
47915     minColumnWidth : 25,
47916
47917     /**
47918      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47919      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47920      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47921      */
47922     autoSizeColumns : false,
47923
47924     /**
47925      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47926      */
47927     autoSizeHeaders : true,
47928
47929     /**
47930      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47931      */
47932     monitorWindowResize : true,
47933
47934     /**
47935      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47936      * rows measured to get a columns size. Default is 0 (all rows).
47937      */
47938     maxRowsToMeasure : 0,
47939
47940     /**
47941      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47942      */
47943     trackMouseOver : true,
47944
47945     /**
47946     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47947     */
47948     
47949     /**
47950     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47951     */
47952     enableDragDrop : false,
47953     
47954     /**
47955     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47956     */
47957     enableColumnMove : true,
47958     
47959     /**
47960     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47961     */
47962     enableColumnHide : true,
47963     
47964     /**
47965     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47966     */
47967     enableRowHeightSync : false,
47968     
47969     /**
47970     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47971     */
47972     stripeRows : true,
47973     
47974     /**
47975     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47976     */
47977     autoHeight : false,
47978
47979     /**
47980      * @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.
47981      */
47982     autoExpandColumn : false,
47983
47984     /**
47985     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47986     * Default is 50.
47987     */
47988     autoExpandMin : 50,
47989
47990     /**
47991     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47992     */
47993     autoExpandMax : 1000,
47994
47995     /**
47996     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47997     */
47998     view : null,
47999
48000     /**
48001     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
48002     */
48003     loadMask : false,
48004     /**
48005     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
48006     */
48007     dropTarget: false,
48008     
48009    
48010     
48011     // private
48012     rendered : false,
48013
48014     /**
48015     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
48016     * of a fixed width. Default is false.
48017     */
48018     /**
48019     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
48020     */
48021     /**
48022      * Called once after all setup has been completed and the grid is ready to be rendered.
48023      * @return {Roo.grid.Grid} this
48024      */
48025     render : function()
48026     {
48027         var c = this.container;
48028         // try to detect autoHeight/width mode
48029         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
48030             this.autoHeight = true;
48031         }
48032         var view = this.getView();
48033         view.init(this);
48034
48035         c.on("click", this.onClick, this);
48036         c.on("dblclick", this.onDblClick, this);
48037         c.on("contextmenu", this.onContextMenu, this);
48038         c.on("keydown", this.onKeyDown, this);
48039
48040         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
48041
48042         this.getSelectionModel().init(this);
48043
48044         view.render();
48045
48046         if(this.loadMask){
48047             this.loadMask = new Roo.LoadMask(this.container,
48048                     Roo.apply({store:this.dataSource}, this.loadMask));
48049         }
48050         
48051         
48052         if (this.toolbar && this.toolbar.xtype) {
48053             this.toolbar.container = this.getView().getHeaderPanel(true);
48054             this.toolbar = new Roo.Toolbar(this.toolbar);
48055         }
48056         if (this.footer && this.footer.xtype) {
48057             this.footer.dataSource = this.getDataSource();
48058             this.footer.container = this.getView().getFooterPanel(true);
48059             this.footer = Roo.factory(this.footer, Roo);
48060         }
48061         if (this.dropTarget && this.dropTarget.xtype) {
48062             delete this.dropTarget.xtype;
48063             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
48064         }
48065         
48066         
48067         this.rendered = true;
48068         this.fireEvent('render', this);
48069         return this;
48070     },
48071
48072         /**
48073          * Reconfigures the grid to use a different Store and Column Model.
48074          * The View will be bound to the new objects and refreshed.
48075          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
48076          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
48077          */
48078     reconfigure : function(dataSource, colModel){
48079         if(this.loadMask){
48080             this.loadMask.destroy();
48081             this.loadMask = new Roo.LoadMask(this.container,
48082                     Roo.apply({store:dataSource}, this.loadMask));
48083         }
48084         this.view.bind(dataSource, colModel);
48085         this.dataSource = dataSource;
48086         this.colModel = colModel;
48087         this.view.refresh(true);
48088     },
48089
48090     // private
48091     onKeyDown : function(e){
48092         this.fireEvent("keydown", e);
48093     },
48094
48095     /**
48096      * Destroy this grid.
48097      * @param {Boolean} removeEl True to remove the element
48098      */
48099     destroy : function(removeEl, keepListeners){
48100         if(this.loadMask){
48101             this.loadMask.destroy();
48102         }
48103         var c = this.container;
48104         c.removeAllListeners();
48105         this.view.destroy();
48106         this.colModel.purgeListeners();
48107         if(!keepListeners){
48108             this.purgeListeners();
48109         }
48110         c.update("");
48111         if(removeEl === true){
48112             c.remove();
48113         }
48114     },
48115
48116     // private
48117     processEvent : function(name, e){
48118         this.fireEvent(name, e);
48119         var t = e.getTarget();
48120         var v = this.view;
48121         var header = v.findHeaderIndex(t);
48122         if(header !== false){
48123             this.fireEvent("header" + name, this, header, e);
48124         }else{
48125             var row = v.findRowIndex(t);
48126             var cell = v.findCellIndex(t);
48127             if(row !== false){
48128                 this.fireEvent("row" + name, this, row, e);
48129                 if(cell !== false){
48130                     this.fireEvent("cell" + name, this, row, cell, e);
48131                 }
48132             }
48133         }
48134     },
48135
48136     // private
48137     onClick : function(e){
48138         this.processEvent("click", e);
48139     },
48140
48141     // private
48142     onContextMenu : function(e, t){
48143         this.processEvent("contextmenu", e);
48144     },
48145
48146     // private
48147     onDblClick : function(e){
48148         this.processEvent("dblclick", e);
48149     },
48150
48151     // private
48152     walkCells : function(row, col, step, fn, scope){
48153         var cm = this.colModel, clen = cm.getColumnCount();
48154         var ds = this.dataSource, rlen = ds.getCount(), first = true;
48155         if(step < 0){
48156             if(col < 0){
48157                 row--;
48158                 first = false;
48159             }
48160             while(row >= 0){
48161                 if(!first){
48162                     col = clen-1;
48163                 }
48164                 first = false;
48165                 while(col >= 0){
48166                     if(fn.call(scope || this, row, col, cm) === true){
48167                         return [row, col];
48168                     }
48169                     col--;
48170                 }
48171                 row--;
48172             }
48173         } else {
48174             if(col >= clen){
48175                 row++;
48176                 first = false;
48177             }
48178             while(row < rlen){
48179                 if(!first){
48180                     col = 0;
48181                 }
48182                 first = false;
48183                 while(col < clen){
48184                     if(fn.call(scope || this, row, col, cm) === true){
48185                         return [row, col];
48186                     }
48187                     col++;
48188                 }
48189                 row++;
48190             }
48191         }
48192         return null;
48193     },
48194
48195     // private
48196     getSelections : function(){
48197         return this.selModel.getSelections();
48198     },
48199
48200     /**
48201      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48202      * but if manual update is required this method will initiate it.
48203      */
48204     autoSize : function(){
48205         if(this.rendered){
48206             this.view.layout();
48207             if(this.view.adjustForScroll){
48208                 this.view.adjustForScroll();
48209             }
48210         }
48211     },
48212
48213     /**
48214      * Returns the grid's underlying element.
48215      * @return {Element} The element
48216      */
48217     getGridEl : function(){
48218         return this.container;
48219     },
48220
48221     // private for compatibility, overridden by editor grid
48222     stopEditing : function(){},
48223
48224     /**
48225      * Returns the grid's SelectionModel.
48226      * @return {SelectionModel}
48227      */
48228     getSelectionModel : function(){
48229         if(!this.selModel){
48230             this.selModel = new Roo.grid.RowSelectionModel();
48231         }
48232         return this.selModel;
48233     },
48234
48235     /**
48236      * Returns the grid's DataSource.
48237      * @return {DataSource}
48238      */
48239     getDataSource : function(){
48240         return this.dataSource;
48241     },
48242
48243     /**
48244      * Returns the grid's ColumnModel.
48245      * @return {ColumnModel}
48246      */
48247     getColumnModel : function(){
48248         return this.colModel;
48249     },
48250
48251     /**
48252      * Returns the grid's GridView object.
48253      * @return {GridView}
48254      */
48255     getView : function(){
48256         if(!this.view){
48257             this.view = new Roo.grid.GridView(this.viewConfig);
48258         }
48259         return this.view;
48260     },
48261     /**
48262      * Called to get grid's drag proxy text, by default returns this.ddText.
48263      * @return {String}
48264      */
48265     getDragDropText : function(){
48266         var count = this.selModel.getCount();
48267         return String.format(this.ddText, count, count == 1 ? '' : 's');
48268     }
48269 });
48270 /**
48271  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48272  * %0 is replaced with the number of selected rows.
48273  * @type String
48274  */
48275 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48276  * Based on:
48277  * Ext JS Library 1.1.1
48278  * Copyright(c) 2006-2007, Ext JS, LLC.
48279  *
48280  * Originally Released Under LGPL - original licence link has changed is not relivant.
48281  *
48282  * Fork - LGPL
48283  * <script type="text/javascript">
48284  */
48285  
48286 Roo.grid.AbstractGridView = function(){
48287         this.grid = null;
48288         
48289         this.events = {
48290             "beforerowremoved" : true,
48291             "beforerowsinserted" : true,
48292             "beforerefresh" : true,
48293             "rowremoved" : true,
48294             "rowsinserted" : true,
48295             "rowupdated" : true,
48296             "refresh" : true
48297         };
48298     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48299 };
48300
48301 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48302     rowClass : "x-grid-row",
48303     cellClass : "x-grid-cell",
48304     tdClass : "x-grid-td",
48305     hdClass : "x-grid-hd",
48306     splitClass : "x-grid-hd-split",
48307     
48308         init: function(grid){
48309         this.grid = grid;
48310                 var cid = this.grid.getGridEl().id;
48311         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48312         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48313         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48314         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48315         },
48316         
48317         getColumnRenderers : function(){
48318         var renderers = [];
48319         var cm = this.grid.colModel;
48320         var colCount = cm.getColumnCount();
48321         for(var i = 0; i < colCount; i++){
48322             renderers[i] = cm.getRenderer(i);
48323         }
48324         return renderers;
48325     },
48326     
48327     getColumnIds : function(){
48328         var ids = [];
48329         var cm = this.grid.colModel;
48330         var colCount = cm.getColumnCount();
48331         for(var i = 0; i < colCount; i++){
48332             ids[i] = cm.getColumnId(i);
48333         }
48334         return ids;
48335     },
48336     
48337     getDataIndexes : function(){
48338         if(!this.indexMap){
48339             this.indexMap = this.buildIndexMap();
48340         }
48341         return this.indexMap.colToData;
48342     },
48343     
48344     getColumnIndexByDataIndex : function(dataIndex){
48345         if(!this.indexMap){
48346             this.indexMap = this.buildIndexMap();
48347         }
48348         return this.indexMap.dataToCol[dataIndex];
48349     },
48350     
48351     /**
48352      * Set a css style for a column dynamically. 
48353      * @param {Number} colIndex The index of the column
48354      * @param {String} name The css property name
48355      * @param {String} value The css value
48356      */
48357     setCSSStyle : function(colIndex, name, value){
48358         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48359         Roo.util.CSS.updateRule(selector, name, value);
48360     },
48361     
48362     generateRules : function(cm){
48363         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48364         Roo.util.CSS.removeStyleSheet(rulesId);
48365         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48366             var cid = cm.getColumnId(i);
48367             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48368                          this.tdSelector, cid, " {\n}\n",
48369                          this.hdSelector, cid, " {\n}\n",
48370                          this.splitSelector, cid, " {\n}\n");
48371         }
48372         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48373     }
48374 });/*
48375  * Based on:
48376  * Ext JS Library 1.1.1
48377  * Copyright(c) 2006-2007, Ext JS, LLC.
48378  *
48379  * Originally Released Under LGPL - original licence link has changed is not relivant.
48380  *
48381  * Fork - LGPL
48382  * <script type="text/javascript">
48383  */
48384
48385 // private
48386 // This is a support class used internally by the Grid components
48387 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48388     this.grid = grid;
48389     this.view = grid.getView();
48390     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48391     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48392     if(hd2){
48393         this.setHandleElId(Roo.id(hd));
48394         this.setOuterHandleElId(Roo.id(hd2));
48395     }
48396     this.scroll = false;
48397 };
48398 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48399     maxDragWidth: 120,
48400     getDragData : function(e){
48401         var t = Roo.lib.Event.getTarget(e);
48402         var h = this.view.findHeaderCell(t);
48403         if(h){
48404             return {ddel: h.firstChild, header:h};
48405         }
48406         return false;
48407     },
48408
48409     onInitDrag : function(e){
48410         this.view.headersDisabled = true;
48411         var clone = this.dragData.ddel.cloneNode(true);
48412         clone.id = Roo.id();
48413         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48414         this.proxy.update(clone);
48415         return true;
48416     },
48417
48418     afterValidDrop : function(){
48419         var v = this.view;
48420         setTimeout(function(){
48421             v.headersDisabled = false;
48422         }, 50);
48423     },
48424
48425     afterInvalidDrop : function(){
48426         var v = this.view;
48427         setTimeout(function(){
48428             v.headersDisabled = false;
48429         }, 50);
48430     }
48431 });
48432 /*
48433  * Based on:
48434  * Ext JS Library 1.1.1
48435  * Copyright(c) 2006-2007, Ext JS, LLC.
48436  *
48437  * Originally Released Under LGPL - original licence link has changed is not relivant.
48438  *
48439  * Fork - LGPL
48440  * <script type="text/javascript">
48441  */
48442 // private
48443 // This is a support class used internally by the Grid components
48444 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48445     this.grid = grid;
48446     this.view = grid.getView();
48447     // split the proxies so they don't interfere with mouse events
48448     this.proxyTop = Roo.DomHelper.append(document.body, {
48449         cls:"col-move-top", html:"&#160;"
48450     }, true);
48451     this.proxyBottom = Roo.DomHelper.append(document.body, {
48452         cls:"col-move-bottom", html:"&#160;"
48453     }, true);
48454     this.proxyTop.hide = this.proxyBottom.hide = function(){
48455         this.setLeftTop(-100,-100);
48456         this.setStyle("visibility", "hidden");
48457     };
48458     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48459     // temporarily disabled
48460     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48461     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48462 };
48463 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48464     proxyOffsets : [-4, -9],
48465     fly: Roo.Element.fly,
48466
48467     getTargetFromEvent : function(e){
48468         var t = Roo.lib.Event.getTarget(e);
48469         var cindex = this.view.findCellIndex(t);
48470         if(cindex !== false){
48471             return this.view.getHeaderCell(cindex);
48472         }
48473         return null;
48474     },
48475
48476     nextVisible : function(h){
48477         var v = this.view, cm = this.grid.colModel;
48478         h = h.nextSibling;
48479         while(h){
48480             if(!cm.isHidden(v.getCellIndex(h))){
48481                 return h;
48482             }
48483             h = h.nextSibling;
48484         }
48485         return null;
48486     },
48487
48488     prevVisible : function(h){
48489         var v = this.view, cm = this.grid.colModel;
48490         h = h.prevSibling;
48491         while(h){
48492             if(!cm.isHidden(v.getCellIndex(h))){
48493                 return h;
48494             }
48495             h = h.prevSibling;
48496         }
48497         return null;
48498     },
48499
48500     positionIndicator : function(h, n, e){
48501         var x = Roo.lib.Event.getPageX(e);
48502         var r = Roo.lib.Dom.getRegion(n.firstChild);
48503         var px, pt, py = r.top + this.proxyOffsets[1];
48504         if((r.right - x) <= (r.right-r.left)/2){
48505             px = r.right+this.view.borderWidth;
48506             pt = "after";
48507         }else{
48508             px = r.left;
48509             pt = "before";
48510         }
48511         var oldIndex = this.view.getCellIndex(h);
48512         var newIndex = this.view.getCellIndex(n);
48513
48514         if(this.grid.colModel.isFixed(newIndex)){
48515             return false;
48516         }
48517
48518         var locked = this.grid.colModel.isLocked(newIndex);
48519
48520         if(pt == "after"){
48521             newIndex++;
48522         }
48523         if(oldIndex < newIndex){
48524             newIndex--;
48525         }
48526         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48527             return false;
48528         }
48529         px +=  this.proxyOffsets[0];
48530         this.proxyTop.setLeftTop(px, py);
48531         this.proxyTop.show();
48532         if(!this.bottomOffset){
48533             this.bottomOffset = this.view.mainHd.getHeight();
48534         }
48535         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48536         this.proxyBottom.show();
48537         return pt;
48538     },
48539
48540     onNodeEnter : function(n, dd, e, data){
48541         if(data.header != n){
48542             this.positionIndicator(data.header, n, e);
48543         }
48544     },
48545
48546     onNodeOver : function(n, dd, e, data){
48547         var result = false;
48548         if(data.header != n){
48549             result = this.positionIndicator(data.header, n, e);
48550         }
48551         if(!result){
48552             this.proxyTop.hide();
48553             this.proxyBottom.hide();
48554         }
48555         return result ? this.dropAllowed : this.dropNotAllowed;
48556     },
48557
48558     onNodeOut : function(n, dd, e, data){
48559         this.proxyTop.hide();
48560         this.proxyBottom.hide();
48561     },
48562
48563     onNodeDrop : function(n, dd, e, data){
48564         var h = data.header;
48565         if(h != n){
48566             var cm = this.grid.colModel;
48567             var x = Roo.lib.Event.getPageX(e);
48568             var r = Roo.lib.Dom.getRegion(n.firstChild);
48569             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48570             var oldIndex = this.view.getCellIndex(h);
48571             var newIndex = this.view.getCellIndex(n);
48572             var locked = cm.isLocked(newIndex);
48573             if(pt == "after"){
48574                 newIndex++;
48575             }
48576             if(oldIndex < newIndex){
48577                 newIndex--;
48578             }
48579             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48580                 return false;
48581             }
48582             cm.setLocked(oldIndex, locked, true);
48583             cm.moveColumn(oldIndex, newIndex);
48584             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48585             return true;
48586         }
48587         return false;
48588     }
48589 });
48590 /*
48591  * Based on:
48592  * Ext JS Library 1.1.1
48593  * Copyright(c) 2006-2007, Ext JS, LLC.
48594  *
48595  * Originally Released Under LGPL - original licence link has changed is not relivant.
48596  *
48597  * Fork - LGPL
48598  * <script type="text/javascript">
48599  */
48600   
48601 /**
48602  * @class Roo.grid.GridView
48603  * @extends Roo.util.Observable
48604  *
48605  * @constructor
48606  * @param {Object} config
48607  */
48608 Roo.grid.GridView = function(config){
48609     Roo.grid.GridView.superclass.constructor.call(this);
48610     this.el = null;
48611
48612     Roo.apply(this, config);
48613 };
48614
48615 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48616
48617     
48618     rowClass : "x-grid-row",
48619
48620     cellClass : "x-grid-col",
48621
48622     tdClass : "x-grid-td",
48623
48624     hdClass : "x-grid-hd",
48625
48626     splitClass : "x-grid-split",
48627
48628     sortClasses : ["sort-asc", "sort-desc"],
48629
48630     enableMoveAnim : false,
48631
48632     hlColor: "C3DAF9",
48633
48634     dh : Roo.DomHelper,
48635
48636     fly : Roo.Element.fly,
48637
48638     css : Roo.util.CSS,
48639
48640     borderWidth: 1,
48641
48642     splitOffset: 3,
48643
48644     scrollIncrement : 22,
48645
48646     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48647
48648     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48649
48650     bind : function(ds, cm){
48651         if(this.ds){
48652             this.ds.un("load", this.onLoad, this);
48653             this.ds.un("datachanged", this.onDataChange, this);
48654             this.ds.un("add", this.onAdd, this);
48655             this.ds.un("remove", this.onRemove, this);
48656             this.ds.un("update", this.onUpdate, this);
48657             this.ds.un("clear", this.onClear, this);
48658         }
48659         if(ds){
48660             ds.on("load", this.onLoad, this);
48661             ds.on("datachanged", this.onDataChange, this);
48662             ds.on("add", this.onAdd, this);
48663             ds.on("remove", this.onRemove, this);
48664             ds.on("update", this.onUpdate, this);
48665             ds.on("clear", this.onClear, this);
48666         }
48667         this.ds = ds;
48668
48669         if(this.cm){
48670             this.cm.un("widthchange", this.onColWidthChange, this);
48671             this.cm.un("headerchange", this.onHeaderChange, this);
48672             this.cm.un("hiddenchange", this.onHiddenChange, this);
48673             this.cm.un("columnmoved", this.onColumnMove, this);
48674             this.cm.un("columnlockchange", this.onColumnLock, this);
48675         }
48676         if(cm){
48677             this.generateRules(cm);
48678             cm.on("widthchange", this.onColWidthChange, this);
48679             cm.on("headerchange", this.onHeaderChange, this);
48680             cm.on("hiddenchange", this.onHiddenChange, this);
48681             cm.on("columnmoved", this.onColumnMove, this);
48682             cm.on("columnlockchange", this.onColumnLock, this);
48683         }
48684         this.cm = cm;
48685     },
48686
48687     init: function(grid){
48688         Roo.grid.GridView.superclass.init.call(this, grid);
48689
48690         this.bind(grid.dataSource, grid.colModel);
48691
48692         grid.on("headerclick", this.handleHeaderClick, this);
48693
48694         if(grid.trackMouseOver){
48695             grid.on("mouseover", this.onRowOver, this);
48696             grid.on("mouseout", this.onRowOut, this);
48697         }
48698         grid.cancelTextSelection = function(){};
48699         this.gridId = grid.id;
48700
48701         var tpls = this.templates || {};
48702
48703         if(!tpls.master){
48704             tpls.master = new Roo.Template(
48705                '<div class="x-grid" hidefocus="true">',
48706                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48707                   '<div class="x-grid-topbar"></div>',
48708                   '<div class="x-grid-scroller"><div></div></div>',
48709                   '<div class="x-grid-locked">',
48710                       '<div class="x-grid-header">{lockedHeader}</div>',
48711                       '<div class="x-grid-body">{lockedBody}</div>',
48712                   "</div>",
48713                   '<div class="x-grid-viewport">',
48714                       '<div class="x-grid-header">{header}</div>',
48715                       '<div class="x-grid-body">{body}</div>',
48716                   "</div>",
48717                   '<div class="x-grid-bottombar"></div>',
48718                  
48719                   '<div class="x-grid-resize-proxy">&#160;</div>',
48720                "</div>"
48721             );
48722             tpls.master.disableformats = true;
48723         }
48724
48725         if(!tpls.header){
48726             tpls.header = new Roo.Template(
48727                '<table border="0" cellspacing="0" cellpadding="0">',
48728                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48729                "</table>{splits}"
48730             );
48731             tpls.header.disableformats = true;
48732         }
48733         tpls.header.compile();
48734
48735         if(!tpls.hcell){
48736             tpls.hcell = new Roo.Template(
48737                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48738                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48739                 "</div></td>"
48740              );
48741              tpls.hcell.disableFormats = true;
48742         }
48743         tpls.hcell.compile();
48744
48745         if(!tpls.hsplit){
48746             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48747             tpls.hsplit.disableFormats = true;
48748         }
48749         tpls.hsplit.compile();
48750
48751         if(!tpls.body){
48752             tpls.body = new Roo.Template(
48753                '<table border="0" cellspacing="0" cellpadding="0">',
48754                "<tbody>{rows}</tbody>",
48755                "</table>"
48756             );
48757             tpls.body.disableFormats = true;
48758         }
48759         tpls.body.compile();
48760
48761         if(!tpls.row){
48762             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48763             tpls.row.disableFormats = true;
48764         }
48765         tpls.row.compile();
48766
48767         if(!tpls.cell){
48768             tpls.cell = new Roo.Template(
48769                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48770                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48771                 "</td>"
48772             );
48773             tpls.cell.disableFormats = true;
48774         }
48775         tpls.cell.compile();
48776
48777         this.templates = tpls;
48778     },
48779
48780     // remap these for backwards compat
48781     onColWidthChange : function(){
48782         this.updateColumns.apply(this, arguments);
48783     },
48784     onHeaderChange : function(){
48785         this.updateHeaders.apply(this, arguments);
48786     }, 
48787     onHiddenChange : function(){
48788         this.handleHiddenChange.apply(this, arguments);
48789     },
48790     onColumnMove : function(){
48791         this.handleColumnMove.apply(this, arguments);
48792     },
48793     onColumnLock : function(){
48794         this.handleLockChange.apply(this, arguments);
48795     },
48796
48797     onDataChange : function(){
48798         this.refresh();
48799         this.updateHeaderSortState();
48800     },
48801
48802     onClear : function(){
48803         this.refresh();
48804     },
48805
48806     onUpdate : function(ds, record){
48807         this.refreshRow(record);
48808     },
48809
48810     refreshRow : function(record){
48811         var ds = this.ds, index;
48812         if(typeof record == 'number'){
48813             index = record;
48814             record = ds.getAt(index);
48815         }else{
48816             index = ds.indexOf(record);
48817         }
48818         this.insertRows(ds, index, index, true);
48819         this.onRemove(ds, record, index+1, true);
48820         this.syncRowHeights(index, index);
48821         this.layout();
48822         this.fireEvent("rowupdated", this, index, record);
48823     },
48824
48825     onAdd : function(ds, records, index){
48826         this.insertRows(ds, index, index + (records.length-1));
48827     },
48828
48829     onRemove : function(ds, record, index, isUpdate){
48830         if(isUpdate !== true){
48831             this.fireEvent("beforerowremoved", this, index, record);
48832         }
48833         var bt = this.getBodyTable(), lt = this.getLockedTable();
48834         if(bt.rows[index]){
48835             bt.firstChild.removeChild(bt.rows[index]);
48836         }
48837         if(lt.rows[index]){
48838             lt.firstChild.removeChild(lt.rows[index]);
48839         }
48840         if(isUpdate !== true){
48841             this.stripeRows(index);
48842             this.syncRowHeights(index, index);
48843             this.layout();
48844             this.fireEvent("rowremoved", this, index, record);
48845         }
48846     },
48847
48848     onLoad : function(){
48849         this.scrollToTop();
48850     },
48851
48852     /**
48853      * Scrolls the grid to the top
48854      */
48855     scrollToTop : function(){
48856         if(this.scroller){
48857             this.scroller.dom.scrollTop = 0;
48858             this.syncScroll();
48859         }
48860     },
48861
48862     /**
48863      * Gets a panel in the header of the grid that can be used for toolbars etc.
48864      * After modifying the contents of this panel a call to grid.autoSize() may be
48865      * required to register any changes in size.
48866      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48867      * @return Roo.Element
48868      */
48869     getHeaderPanel : function(doShow){
48870         if(doShow){
48871             this.headerPanel.show();
48872         }
48873         return this.headerPanel;
48874     },
48875
48876     /**
48877      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48878      * After modifying the contents of this panel a call to grid.autoSize() may be
48879      * required to register any changes in size.
48880      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48881      * @return Roo.Element
48882      */
48883     getFooterPanel : function(doShow){
48884         if(doShow){
48885             this.footerPanel.show();
48886         }
48887         return this.footerPanel;
48888     },
48889
48890     initElements : function(){
48891         var E = Roo.Element;
48892         var el = this.grid.getGridEl().dom.firstChild;
48893         var cs = el.childNodes;
48894
48895         this.el = new E(el);
48896         
48897          this.focusEl = new E(el.firstChild);
48898         this.focusEl.swallowEvent("click", true);
48899         
48900         this.headerPanel = new E(cs[1]);
48901         this.headerPanel.enableDisplayMode("block");
48902
48903         this.scroller = new E(cs[2]);
48904         this.scrollSizer = new E(this.scroller.dom.firstChild);
48905
48906         this.lockedWrap = new E(cs[3]);
48907         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48908         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48909
48910         this.mainWrap = new E(cs[4]);
48911         this.mainHd = new E(this.mainWrap.dom.firstChild);
48912         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48913
48914         this.footerPanel = new E(cs[5]);
48915         this.footerPanel.enableDisplayMode("block");
48916
48917         this.resizeProxy = new E(cs[6]);
48918
48919         this.headerSelector = String.format(
48920            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48921            this.lockedHd.id, this.mainHd.id
48922         );
48923
48924         this.splitterSelector = String.format(
48925            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48926            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48927         );
48928     },
48929     idToCssName : function(s)
48930     {
48931         return s.replace(/[^a-z0-9]+/ig, '-');
48932     },
48933
48934     getHeaderCell : function(index){
48935         return Roo.DomQuery.select(this.headerSelector)[index];
48936     },
48937
48938     getHeaderCellMeasure : function(index){
48939         return this.getHeaderCell(index).firstChild;
48940     },
48941
48942     getHeaderCellText : function(index){
48943         return this.getHeaderCell(index).firstChild.firstChild;
48944     },
48945
48946     getLockedTable : function(){
48947         return this.lockedBody.dom.firstChild;
48948     },
48949
48950     getBodyTable : function(){
48951         return this.mainBody.dom.firstChild;
48952     },
48953
48954     getLockedRow : function(index){
48955         return this.getLockedTable().rows[index];
48956     },
48957
48958     getRow : function(index){
48959         return this.getBodyTable().rows[index];
48960     },
48961
48962     getRowComposite : function(index){
48963         if(!this.rowEl){
48964             this.rowEl = new Roo.CompositeElementLite();
48965         }
48966         var els = [], lrow, mrow;
48967         if(lrow = this.getLockedRow(index)){
48968             els.push(lrow);
48969         }
48970         if(mrow = this.getRow(index)){
48971             els.push(mrow);
48972         }
48973         this.rowEl.elements = els;
48974         return this.rowEl;
48975     },
48976     /**
48977      * Gets the 'td' of the cell
48978      * 
48979      * @param {Integer} rowIndex row to select
48980      * @param {Integer} colIndex column to select
48981      * 
48982      * @return {Object} 
48983      */
48984     getCell : function(rowIndex, colIndex){
48985         var locked = this.cm.getLockedCount();
48986         var source;
48987         if(colIndex < locked){
48988             source = this.lockedBody.dom.firstChild;
48989         }else{
48990             source = this.mainBody.dom.firstChild;
48991             colIndex -= locked;
48992         }
48993         return source.rows[rowIndex].childNodes[colIndex];
48994     },
48995
48996     getCellText : function(rowIndex, colIndex){
48997         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48998     },
48999
49000     getCellBox : function(cell){
49001         var b = this.fly(cell).getBox();
49002         if(Roo.isOpera){ // opera fails to report the Y
49003             b.y = cell.offsetTop + this.mainBody.getY();
49004         }
49005         return b;
49006     },
49007
49008     getCellIndex : function(cell){
49009         var id = String(cell.className).match(this.cellRE);
49010         if(id){
49011             return parseInt(id[1], 10);
49012         }
49013         return 0;
49014     },
49015
49016     findHeaderIndex : function(n){
49017         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49018         return r ? this.getCellIndex(r) : false;
49019     },
49020
49021     findHeaderCell : function(n){
49022         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49023         return r ? r : false;
49024     },
49025
49026     findRowIndex : function(n){
49027         if(!n){
49028             return false;
49029         }
49030         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
49031         return r ? r.rowIndex : false;
49032     },
49033
49034     findCellIndex : function(node){
49035         var stop = this.el.dom;
49036         while(node && node != stop){
49037             if(this.findRE.test(node.className)){
49038                 return this.getCellIndex(node);
49039             }
49040             node = node.parentNode;
49041         }
49042         return false;
49043     },
49044
49045     getColumnId : function(index){
49046         return this.cm.getColumnId(index);
49047     },
49048
49049     getSplitters : function()
49050     {
49051         if(this.splitterSelector){
49052            return Roo.DomQuery.select(this.splitterSelector);
49053         }else{
49054             return null;
49055       }
49056     },
49057
49058     getSplitter : function(index){
49059         return this.getSplitters()[index];
49060     },
49061
49062     onRowOver : function(e, t){
49063         var row;
49064         if((row = this.findRowIndex(t)) !== false){
49065             this.getRowComposite(row).addClass("x-grid-row-over");
49066         }
49067     },
49068
49069     onRowOut : function(e, t){
49070         var row;
49071         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
49072             this.getRowComposite(row).removeClass("x-grid-row-over");
49073         }
49074     },
49075
49076     renderHeaders : function(){
49077         var cm = this.cm;
49078         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
49079         var cb = [], lb = [], sb = [], lsb = [], p = {};
49080         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49081             p.cellId = "x-grid-hd-0-" + i;
49082             p.splitId = "x-grid-csplit-0-" + i;
49083             p.id = cm.getColumnId(i);
49084             p.title = cm.getColumnTooltip(i) || "";
49085             p.value = cm.getColumnHeader(i) || "";
49086             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
49087             if(!cm.isLocked(i)){
49088                 cb[cb.length] = ct.apply(p);
49089                 sb[sb.length] = st.apply(p);
49090             }else{
49091                 lb[lb.length] = ct.apply(p);
49092                 lsb[lsb.length] = st.apply(p);
49093             }
49094         }
49095         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
49096                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
49097     },
49098
49099     updateHeaders : function(){
49100         var html = this.renderHeaders();
49101         this.lockedHd.update(html[0]);
49102         this.mainHd.update(html[1]);
49103     },
49104
49105     /**
49106      * Focuses the specified row.
49107      * @param {Number} row The row index
49108      */
49109     focusRow : function(row)
49110     {
49111         //Roo.log('GridView.focusRow');
49112         var x = this.scroller.dom.scrollLeft;
49113         this.focusCell(row, 0, false);
49114         this.scroller.dom.scrollLeft = x;
49115     },
49116
49117     /**
49118      * Focuses the specified cell.
49119      * @param {Number} row The row index
49120      * @param {Number} col The column index
49121      * @param {Boolean} hscroll false to disable horizontal scrolling
49122      */
49123     focusCell : function(row, col, hscroll)
49124     {
49125         //Roo.log('GridView.focusCell');
49126         var el = this.ensureVisible(row, col, hscroll);
49127         this.focusEl.alignTo(el, "tl-tl");
49128         if(Roo.isGecko){
49129             this.focusEl.focus();
49130         }else{
49131             this.focusEl.focus.defer(1, this.focusEl);
49132         }
49133     },
49134
49135     /**
49136      * Scrolls the specified cell into view
49137      * @param {Number} row The row index
49138      * @param {Number} col The column index
49139      * @param {Boolean} hscroll false to disable horizontal scrolling
49140      */
49141     ensureVisible : function(row, col, hscroll)
49142     {
49143         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
49144         //return null; //disable for testing.
49145         if(typeof row != "number"){
49146             row = row.rowIndex;
49147         }
49148         if(row < 0 && row >= this.ds.getCount()){
49149             return  null;
49150         }
49151         col = (col !== undefined ? col : 0);
49152         var cm = this.grid.colModel;
49153         while(cm.isHidden(col)){
49154             col++;
49155         }
49156
49157         var el = this.getCell(row, col);
49158         if(!el){
49159             return null;
49160         }
49161         var c = this.scroller.dom;
49162
49163         var ctop = parseInt(el.offsetTop, 10);
49164         var cleft = parseInt(el.offsetLeft, 10);
49165         var cbot = ctop + el.offsetHeight;
49166         var cright = cleft + el.offsetWidth;
49167         
49168         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
49169         var stop = parseInt(c.scrollTop, 10);
49170         var sleft = parseInt(c.scrollLeft, 10);
49171         var sbot = stop + ch;
49172         var sright = sleft + c.clientWidth;
49173         /*
49174         Roo.log('GridView.ensureVisible:' +
49175                 ' ctop:' + ctop +
49176                 ' c.clientHeight:' + c.clientHeight +
49177                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
49178                 ' stop:' + stop +
49179                 ' cbot:' + cbot +
49180                 ' sbot:' + sbot +
49181                 ' ch:' + ch  
49182                 );
49183         */
49184         if(ctop < stop){
49185              c.scrollTop = ctop;
49186             //Roo.log("set scrolltop to ctop DISABLE?");
49187         }else if(cbot > sbot){
49188             //Roo.log("set scrolltop to cbot-ch");
49189             c.scrollTop = cbot-ch;
49190         }
49191         
49192         if(hscroll !== false){
49193             if(cleft < sleft){
49194                 c.scrollLeft = cleft;
49195             }else if(cright > sright){
49196                 c.scrollLeft = cright-c.clientWidth;
49197             }
49198         }
49199          
49200         return el;
49201     },
49202
49203     updateColumns : function(){
49204         this.grid.stopEditing();
49205         var cm = this.grid.colModel, colIds = this.getColumnIds();
49206         //var totalWidth = cm.getTotalWidth();
49207         var pos = 0;
49208         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49209             //if(cm.isHidden(i)) continue;
49210             var w = cm.getColumnWidth(i);
49211             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49212             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49213         }
49214         this.updateSplitters();
49215     },
49216
49217     generateRules : function(cm){
49218         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49219         Roo.util.CSS.removeStyleSheet(rulesId);
49220         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49221             var cid = cm.getColumnId(i);
49222             var align = '';
49223             if(cm.config[i].align){
49224                 align = 'text-align:'+cm.config[i].align+';';
49225             }
49226             var hidden = '';
49227             if(cm.isHidden(i)){
49228                 hidden = 'display:none;';
49229             }
49230             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49231             ruleBuf.push(
49232                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49233                     this.hdSelector, cid, " {\n", align, width, "}\n",
49234                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49235                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49236         }
49237         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49238     },
49239
49240     updateSplitters : function(){
49241         var cm = this.cm, s = this.getSplitters();
49242         if(s){ // splitters not created yet
49243             var pos = 0, locked = true;
49244             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49245                 if(cm.isHidden(i)) continue;
49246                 var w = cm.getColumnWidth(i); // make sure it's a number
49247                 if(!cm.isLocked(i) && locked){
49248                     pos = 0;
49249                     locked = false;
49250                 }
49251                 pos += w;
49252                 s[i].style.left = (pos-this.splitOffset) + "px";
49253             }
49254         }
49255     },
49256
49257     handleHiddenChange : function(colModel, colIndex, hidden){
49258         if(hidden){
49259             this.hideColumn(colIndex);
49260         }else{
49261             this.unhideColumn(colIndex);
49262         }
49263     },
49264
49265     hideColumn : function(colIndex){
49266         var cid = this.getColumnId(colIndex);
49267         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49268         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49269         if(Roo.isSafari){
49270             this.updateHeaders();
49271         }
49272         this.updateSplitters();
49273         this.layout();
49274     },
49275
49276     unhideColumn : function(colIndex){
49277         var cid = this.getColumnId(colIndex);
49278         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49279         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49280
49281         if(Roo.isSafari){
49282             this.updateHeaders();
49283         }
49284         this.updateSplitters();
49285         this.layout();
49286     },
49287
49288     insertRows : function(dm, firstRow, lastRow, isUpdate){
49289         if(firstRow == 0 && lastRow == dm.getCount()-1){
49290             this.refresh();
49291         }else{
49292             if(!isUpdate){
49293                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49294             }
49295             var s = this.getScrollState();
49296             var markup = this.renderRows(firstRow, lastRow);
49297             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49298             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49299             this.restoreScroll(s);
49300             if(!isUpdate){
49301                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49302                 this.syncRowHeights(firstRow, lastRow);
49303                 this.stripeRows(firstRow);
49304                 this.layout();
49305             }
49306         }
49307     },
49308
49309     bufferRows : function(markup, target, index){
49310         var before = null, trows = target.rows, tbody = target.tBodies[0];
49311         if(index < trows.length){
49312             before = trows[index];
49313         }
49314         var b = document.createElement("div");
49315         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49316         var rows = b.firstChild.rows;
49317         for(var i = 0, len = rows.length; i < len; i++){
49318             if(before){
49319                 tbody.insertBefore(rows[0], before);
49320             }else{
49321                 tbody.appendChild(rows[0]);
49322             }
49323         }
49324         b.innerHTML = "";
49325         b = null;
49326     },
49327
49328     deleteRows : function(dm, firstRow, lastRow){
49329         if(dm.getRowCount()<1){
49330             this.fireEvent("beforerefresh", this);
49331             this.mainBody.update("");
49332             this.lockedBody.update("");
49333             this.fireEvent("refresh", this);
49334         }else{
49335             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49336             var bt = this.getBodyTable();
49337             var tbody = bt.firstChild;
49338             var rows = bt.rows;
49339             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49340                 tbody.removeChild(rows[firstRow]);
49341             }
49342             this.stripeRows(firstRow);
49343             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49344         }
49345     },
49346
49347     updateRows : function(dataSource, firstRow, lastRow){
49348         var s = this.getScrollState();
49349         this.refresh();
49350         this.restoreScroll(s);
49351     },
49352
49353     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49354         if(!noRefresh){
49355            this.refresh();
49356         }
49357         this.updateHeaderSortState();
49358     },
49359
49360     getScrollState : function(){
49361         
49362         var sb = this.scroller.dom;
49363         return {left: sb.scrollLeft, top: sb.scrollTop};
49364     },
49365
49366     stripeRows : function(startRow){
49367         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49368             return;
49369         }
49370         startRow = startRow || 0;
49371         var rows = this.getBodyTable().rows;
49372         var lrows = this.getLockedTable().rows;
49373         var cls = ' x-grid-row-alt ';
49374         for(var i = startRow, len = rows.length; i < len; i++){
49375             var row = rows[i], lrow = lrows[i];
49376             var isAlt = ((i+1) % 2 == 0);
49377             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49378             if(isAlt == hasAlt){
49379                 continue;
49380             }
49381             if(isAlt){
49382                 row.className += " x-grid-row-alt";
49383             }else{
49384                 row.className = row.className.replace("x-grid-row-alt", "");
49385             }
49386             if(lrow){
49387                 lrow.className = row.className;
49388             }
49389         }
49390     },
49391
49392     restoreScroll : function(state){
49393         //Roo.log('GridView.restoreScroll');
49394         var sb = this.scroller.dom;
49395         sb.scrollLeft = state.left;
49396         sb.scrollTop = state.top;
49397         this.syncScroll();
49398     },
49399
49400     syncScroll : function(){
49401         //Roo.log('GridView.syncScroll');
49402         var sb = this.scroller.dom;
49403         var sh = this.mainHd.dom;
49404         var bs = this.mainBody.dom;
49405         var lv = this.lockedBody.dom;
49406         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49407         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49408     },
49409
49410     handleScroll : function(e){
49411         this.syncScroll();
49412         var sb = this.scroller.dom;
49413         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49414         e.stopEvent();
49415     },
49416
49417     handleWheel : function(e){
49418         var d = e.getWheelDelta();
49419         this.scroller.dom.scrollTop -= d*22;
49420         // set this here to prevent jumpy scrolling on large tables
49421         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49422         e.stopEvent();
49423     },
49424
49425     renderRows : function(startRow, endRow){
49426         // pull in all the crap needed to render rows
49427         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49428         var colCount = cm.getColumnCount();
49429
49430         if(ds.getCount() < 1){
49431             return ["", ""];
49432         }
49433
49434         // build a map for all the columns
49435         var cs = [];
49436         for(var i = 0; i < colCount; i++){
49437             var name = cm.getDataIndex(i);
49438             cs[i] = {
49439                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49440                 renderer : cm.getRenderer(i),
49441                 id : cm.getColumnId(i),
49442                 locked : cm.isLocked(i)
49443             };
49444         }
49445
49446         startRow = startRow || 0;
49447         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49448
49449         // records to render
49450         var rs = ds.getRange(startRow, endRow);
49451
49452         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49453     },
49454
49455     // As much as I hate to duplicate code, this was branched because FireFox really hates
49456     // [].join("") on strings. The performance difference was substantial enough to
49457     // branch this function
49458     doRender : Roo.isGecko ?
49459             function(cs, rs, ds, startRow, colCount, stripe){
49460                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49461                 // buffers
49462                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49463                 
49464                 var hasListener = this.grid.hasListener('rowclass');
49465                 var rowcfg = {};
49466                 for(var j = 0, len = rs.length; j < len; j++){
49467                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49468                     for(var i = 0; i < colCount; i++){
49469                         c = cs[i];
49470                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49471                         p.id = c.id;
49472                         p.css = p.attr = "";
49473                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49474                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49475                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49476                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49477                         }
49478                         var markup = ct.apply(p);
49479                         if(!c.locked){
49480                             cb+= markup;
49481                         }else{
49482                             lcb+= markup;
49483                         }
49484                     }
49485                     var alt = [];
49486                     if(stripe && ((rowIndex+1) % 2 == 0)){
49487                         alt.push("x-grid-row-alt")
49488                     }
49489                     if(r.dirty){
49490                         alt.push(  " x-grid-dirty-row");
49491                     }
49492                     rp.cells = lcb;
49493                     if(this.getRowClass){
49494                         alt.push(this.getRowClass(r, rowIndex));
49495                     }
49496                     if (hasListener) {
49497                         rowcfg = {
49498                              
49499                             record: r,
49500                             rowIndex : rowIndex,
49501                             rowClass : ''
49502                         }
49503                         this.grid.fireEvent('rowclass', this, rowcfg);
49504                         alt.push(rowcfg.rowClass);
49505                     }
49506                     rp.alt = alt.join(" ");
49507                     lbuf+= rt.apply(rp);
49508                     rp.cells = cb;
49509                     buf+=  rt.apply(rp);
49510                 }
49511                 return [lbuf, buf];
49512             } :
49513             function(cs, rs, ds, startRow, colCount, stripe){
49514                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49515                 // buffers
49516                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49517                 var hasListener = this.grid.hasListener('rowclass');
49518  
49519                 var rowcfg = {};
49520                 for(var j = 0, len = rs.length; j < len; j++){
49521                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49522                     for(var i = 0; i < colCount; i++){
49523                         c = cs[i];
49524                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49525                         p.id = c.id;
49526                         p.css = p.attr = "";
49527                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49528                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49529                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49530                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49531                         }
49532                         
49533                         var markup = ct.apply(p);
49534                         if(!c.locked){
49535                             cb[cb.length] = markup;
49536                         }else{
49537                             lcb[lcb.length] = markup;
49538                         }
49539                     }
49540                     var alt = [];
49541                     if(stripe && ((rowIndex+1) % 2 == 0)){
49542                         alt.push( "x-grid-row-alt");
49543                     }
49544                     if(r.dirty){
49545                         alt.push(" x-grid-dirty-row");
49546                     }
49547                     rp.cells = lcb;
49548                     if(this.getRowClass){
49549                         alt.push( this.getRowClass(r, rowIndex));
49550                     }
49551                     if (hasListener) {
49552                         rowcfg = {
49553                              
49554                             record: r,
49555                             rowIndex : rowIndex,
49556                             rowClass : ''
49557                         }
49558                         this.grid.fireEvent('rowclass', this, rowcfg);
49559                         alt.push(rowcfg.rowClass);
49560                     }
49561                     rp.alt = alt.join(" ");
49562                     rp.cells = lcb.join("");
49563                     lbuf[lbuf.length] = rt.apply(rp);
49564                     rp.cells = cb.join("");
49565                     buf[buf.length] =  rt.apply(rp);
49566                 }
49567                 return [lbuf.join(""), buf.join("")];
49568             },
49569
49570     renderBody : function(){
49571         var markup = this.renderRows();
49572         var bt = this.templates.body;
49573         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49574     },
49575
49576     /**
49577      * Refreshes the grid
49578      * @param {Boolean} headersToo
49579      */
49580     refresh : function(headersToo){
49581         this.fireEvent("beforerefresh", this);
49582         this.grid.stopEditing();
49583         var result = this.renderBody();
49584         this.lockedBody.update(result[0]);
49585         this.mainBody.update(result[1]);
49586         if(headersToo === true){
49587             this.updateHeaders();
49588             this.updateColumns();
49589             this.updateSplitters();
49590             this.updateHeaderSortState();
49591         }
49592         this.syncRowHeights();
49593         this.layout();
49594         this.fireEvent("refresh", this);
49595     },
49596
49597     handleColumnMove : function(cm, oldIndex, newIndex){
49598         this.indexMap = null;
49599         var s = this.getScrollState();
49600         this.refresh(true);
49601         this.restoreScroll(s);
49602         this.afterMove(newIndex);
49603     },
49604
49605     afterMove : function(colIndex){
49606         if(this.enableMoveAnim && Roo.enableFx){
49607             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49608         }
49609         // if multisort - fix sortOrder, and reload..
49610         if (this.grid.dataSource.multiSort) {
49611             // the we can call sort again..
49612             var dm = this.grid.dataSource;
49613             var cm = this.grid.colModel;
49614             var so = [];
49615             for(var i = 0; i < cm.config.length; i++ ) {
49616                 
49617                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49618                     continue; // dont' bother, it's not in sort list or being set.
49619                 }
49620                 
49621                 so.push(cm.config[i].dataIndex);
49622             };
49623             dm.sortOrder = so;
49624             dm.load(dm.lastOptions);
49625             
49626             
49627         }
49628         
49629     },
49630
49631     updateCell : function(dm, rowIndex, dataIndex){
49632         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49633         if(typeof colIndex == "undefined"){ // not present in grid
49634             return;
49635         }
49636         var cm = this.grid.colModel;
49637         var cell = this.getCell(rowIndex, colIndex);
49638         var cellText = this.getCellText(rowIndex, colIndex);
49639
49640         var p = {
49641             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49642             id : cm.getColumnId(colIndex),
49643             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49644         };
49645         var renderer = cm.getRenderer(colIndex);
49646         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49647         if(typeof val == "undefined" || val === "") val = "&#160;";
49648         cellText.innerHTML = val;
49649         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49650         this.syncRowHeights(rowIndex, rowIndex);
49651     },
49652
49653     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49654         var maxWidth = 0;
49655         if(this.grid.autoSizeHeaders){
49656             var h = this.getHeaderCellMeasure(colIndex);
49657             maxWidth = Math.max(maxWidth, h.scrollWidth);
49658         }
49659         var tb, index;
49660         if(this.cm.isLocked(colIndex)){
49661             tb = this.getLockedTable();
49662             index = colIndex;
49663         }else{
49664             tb = this.getBodyTable();
49665             index = colIndex - this.cm.getLockedCount();
49666         }
49667         if(tb && tb.rows){
49668             var rows = tb.rows;
49669             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49670             for(var i = 0; i < stopIndex; i++){
49671                 var cell = rows[i].childNodes[index].firstChild;
49672                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49673             }
49674         }
49675         return maxWidth + /*margin for error in IE*/ 5;
49676     },
49677     /**
49678      * Autofit a column to its content.
49679      * @param {Number} colIndex
49680      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49681      */
49682      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49683          if(this.cm.isHidden(colIndex)){
49684              return; // can't calc a hidden column
49685          }
49686         if(forceMinSize){
49687             var cid = this.cm.getColumnId(colIndex);
49688             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49689            if(this.grid.autoSizeHeaders){
49690                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49691            }
49692         }
49693         var newWidth = this.calcColumnWidth(colIndex);
49694         this.cm.setColumnWidth(colIndex,
49695             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49696         if(!suppressEvent){
49697             this.grid.fireEvent("columnresize", colIndex, newWidth);
49698         }
49699     },
49700
49701     /**
49702      * Autofits all columns to their content and then expands to fit any extra space in the grid
49703      */
49704      autoSizeColumns : function(){
49705         var cm = this.grid.colModel;
49706         var colCount = cm.getColumnCount();
49707         for(var i = 0; i < colCount; i++){
49708             this.autoSizeColumn(i, true, true);
49709         }
49710         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49711             this.fitColumns();
49712         }else{
49713             this.updateColumns();
49714             this.layout();
49715         }
49716     },
49717
49718     /**
49719      * Autofits all columns to the grid's width proportionate with their current size
49720      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49721      */
49722     fitColumns : function(reserveScrollSpace){
49723         var cm = this.grid.colModel;
49724         var colCount = cm.getColumnCount();
49725         var cols = [];
49726         var width = 0;
49727         var i, w;
49728         for (i = 0; i < colCount; i++){
49729             if(!cm.isHidden(i) && !cm.isFixed(i)){
49730                 w = cm.getColumnWidth(i);
49731                 cols.push(i);
49732                 cols.push(w);
49733                 width += w;
49734             }
49735         }
49736         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49737         if(reserveScrollSpace){
49738             avail -= 17;
49739         }
49740         var frac = (avail - cm.getTotalWidth())/width;
49741         while (cols.length){
49742             w = cols.pop();
49743             i = cols.pop();
49744             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49745         }
49746         this.updateColumns();
49747         this.layout();
49748     },
49749
49750     onRowSelect : function(rowIndex){
49751         var row = this.getRowComposite(rowIndex);
49752         row.addClass("x-grid-row-selected");
49753     },
49754
49755     onRowDeselect : function(rowIndex){
49756         var row = this.getRowComposite(rowIndex);
49757         row.removeClass("x-grid-row-selected");
49758     },
49759
49760     onCellSelect : function(row, col){
49761         var cell = this.getCell(row, col);
49762         if(cell){
49763             Roo.fly(cell).addClass("x-grid-cell-selected");
49764         }
49765     },
49766
49767     onCellDeselect : function(row, col){
49768         var cell = this.getCell(row, col);
49769         if(cell){
49770             Roo.fly(cell).removeClass("x-grid-cell-selected");
49771         }
49772     },
49773
49774     updateHeaderSortState : function(){
49775         
49776         // sort state can be single { field: xxx, direction : yyy}
49777         // or   { xxx=>ASC , yyy : DESC ..... }
49778         
49779         var mstate = {};
49780         if (!this.ds.multiSort) { 
49781             var state = this.ds.getSortState();
49782             if(!state){
49783                 return;
49784             }
49785             mstate[state.field] = state.direction;
49786             // FIXME... - this is not used here.. but might be elsewhere..
49787             this.sortState = state;
49788             
49789         } else {
49790             mstate = this.ds.sortToggle;
49791         }
49792         //remove existing sort classes..
49793         
49794         var sc = this.sortClasses;
49795         var hds = this.el.select(this.headerSelector).removeClass(sc);
49796         
49797         for(var f in mstate) {
49798         
49799             var sortColumn = this.cm.findColumnIndex(f);
49800             
49801             if(sortColumn != -1){
49802                 var sortDir = mstate[f];        
49803                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49804             }
49805         }
49806         
49807          
49808         
49809     },
49810
49811
49812     handleHeaderClick : function(g, index){
49813         if(this.headersDisabled){
49814             return;
49815         }
49816         var dm = g.dataSource, cm = g.colModel;
49817         if(!cm.isSortable(index)){
49818             return;
49819         }
49820         g.stopEditing();
49821         
49822         if (dm.multiSort) {
49823             // update the sortOrder
49824             var so = [];
49825             for(var i = 0; i < cm.config.length; i++ ) {
49826                 
49827                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49828                     continue; // dont' bother, it's not in sort list or being set.
49829                 }
49830                 
49831                 so.push(cm.config[i].dataIndex);
49832             };
49833             dm.sortOrder = so;
49834         }
49835         
49836         
49837         dm.sort(cm.getDataIndex(index));
49838     },
49839
49840
49841     destroy : function(){
49842         if(this.colMenu){
49843             this.colMenu.removeAll();
49844             Roo.menu.MenuMgr.unregister(this.colMenu);
49845             this.colMenu.getEl().remove();
49846             delete this.colMenu;
49847         }
49848         if(this.hmenu){
49849             this.hmenu.removeAll();
49850             Roo.menu.MenuMgr.unregister(this.hmenu);
49851             this.hmenu.getEl().remove();
49852             delete this.hmenu;
49853         }
49854         if(this.grid.enableColumnMove){
49855             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49856             if(dds){
49857                 for(var dd in dds){
49858                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49859                         var elid = dds[dd].dragElId;
49860                         dds[dd].unreg();
49861                         Roo.get(elid).remove();
49862                     } else if(dds[dd].config.isTarget){
49863                         dds[dd].proxyTop.remove();
49864                         dds[dd].proxyBottom.remove();
49865                         dds[dd].unreg();
49866                     }
49867                     if(Roo.dd.DDM.locationCache[dd]){
49868                         delete Roo.dd.DDM.locationCache[dd];
49869                     }
49870                 }
49871                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49872             }
49873         }
49874         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49875         this.bind(null, null);
49876         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49877     },
49878
49879     handleLockChange : function(){
49880         this.refresh(true);
49881     },
49882
49883     onDenyColumnLock : function(){
49884
49885     },
49886
49887     onDenyColumnHide : function(){
49888
49889     },
49890
49891     handleHdMenuClick : function(item){
49892         var index = this.hdCtxIndex;
49893         var cm = this.cm, ds = this.ds;
49894         switch(item.id){
49895             case "asc":
49896                 ds.sort(cm.getDataIndex(index), "ASC");
49897                 break;
49898             case "desc":
49899                 ds.sort(cm.getDataIndex(index), "DESC");
49900                 break;
49901             case "lock":
49902                 var lc = cm.getLockedCount();
49903                 if(cm.getColumnCount(true) <= lc+1){
49904                     this.onDenyColumnLock();
49905                     return;
49906                 }
49907                 if(lc != index){
49908                     cm.setLocked(index, true, true);
49909                     cm.moveColumn(index, lc);
49910                     this.grid.fireEvent("columnmove", index, lc);
49911                 }else{
49912                     cm.setLocked(index, true);
49913                 }
49914             break;
49915             case "unlock":
49916                 var lc = cm.getLockedCount();
49917                 if((lc-1) != index){
49918                     cm.setLocked(index, false, true);
49919                     cm.moveColumn(index, lc-1);
49920                     this.grid.fireEvent("columnmove", index, lc-1);
49921                 }else{
49922                     cm.setLocked(index, false);
49923                 }
49924             break;
49925             default:
49926                 index = cm.getIndexById(item.id.substr(4));
49927                 if(index != -1){
49928                     if(item.checked && cm.getColumnCount(true) <= 1){
49929                         this.onDenyColumnHide();
49930                         return false;
49931                     }
49932                     cm.setHidden(index, item.checked);
49933                 }
49934         }
49935         return true;
49936     },
49937
49938     beforeColMenuShow : function(){
49939         var cm = this.cm,  colCount = cm.getColumnCount();
49940         this.colMenu.removeAll();
49941         for(var i = 0; i < colCount; i++){
49942             this.colMenu.add(new Roo.menu.CheckItem({
49943                 id: "col-"+cm.getColumnId(i),
49944                 text: cm.getColumnHeader(i),
49945                 checked: !cm.isHidden(i),
49946                 hideOnClick:false
49947             }));
49948         }
49949     },
49950
49951     handleHdCtx : function(g, index, e){
49952         e.stopEvent();
49953         var hd = this.getHeaderCell(index);
49954         this.hdCtxIndex = index;
49955         var ms = this.hmenu.items, cm = this.cm;
49956         ms.get("asc").setDisabled(!cm.isSortable(index));
49957         ms.get("desc").setDisabled(!cm.isSortable(index));
49958         if(this.grid.enableColLock !== false){
49959             ms.get("lock").setDisabled(cm.isLocked(index));
49960             ms.get("unlock").setDisabled(!cm.isLocked(index));
49961         }
49962         this.hmenu.show(hd, "tl-bl");
49963     },
49964
49965     handleHdOver : function(e){
49966         var hd = this.findHeaderCell(e.getTarget());
49967         if(hd && !this.headersDisabled){
49968             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49969                this.fly(hd).addClass("x-grid-hd-over");
49970             }
49971         }
49972     },
49973
49974     handleHdOut : function(e){
49975         var hd = this.findHeaderCell(e.getTarget());
49976         if(hd){
49977             this.fly(hd).removeClass("x-grid-hd-over");
49978         }
49979     },
49980
49981     handleSplitDblClick : function(e, t){
49982         var i = this.getCellIndex(t);
49983         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49984             this.autoSizeColumn(i, true);
49985             this.layout();
49986         }
49987     },
49988
49989     render : function(){
49990
49991         var cm = this.cm;
49992         var colCount = cm.getColumnCount();
49993
49994         if(this.grid.monitorWindowResize === true){
49995             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49996         }
49997         var header = this.renderHeaders();
49998         var body = this.templates.body.apply({rows:""});
49999         var html = this.templates.master.apply({
50000             lockedBody: body,
50001             body: body,
50002             lockedHeader: header[0],
50003             header: header[1]
50004         });
50005
50006         //this.updateColumns();
50007
50008         this.grid.getGridEl().dom.innerHTML = html;
50009
50010         this.initElements();
50011         
50012         // a kludge to fix the random scolling effect in webkit
50013         this.el.on("scroll", function() {
50014             this.el.dom.scrollTop=0; // hopefully not recursive..
50015         },this);
50016
50017         this.scroller.on("scroll", this.handleScroll, this);
50018         this.lockedBody.on("mousewheel", this.handleWheel, this);
50019         this.mainBody.on("mousewheel", this.handleWheel, this);
50020
50021         this.mainHd.on("mouseover", this.handleHdOver, this);
50022         this.mainHd.on("mouseout", this.handleHdOut, this);
50023         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
50024                 {delegate: "."+this.splitClass});
50025
50026         this.lockedHd.on("mouseover", this.handleHdOver, this);
50027         this.lockedHd.on("mouseout", this.handleHdOut, this);
50028         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
50029                 {delegate: "."+this.splitClass});
50030
50031         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
50032             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50033         }
50034
50035         this.updateSplitters();
50036
50037         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
50038             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50039             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50040         }
50041
50042         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
50043             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
50044             this.hmenu.add(
50045                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
50046                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
50047             );
50048             if(this.grid.enableColLock !== false){
50049                 this.hmenu.add('-',
50050                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
50051                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
50052                 );
50053             }
50054             if(this.grid.enableColumnHide !== false){
50055
50056                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
50057                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
50058                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
50059
50060                 this.hmenu.add('-',
50061                     {id:"columns", text: this.columnsText, menu: this.colMenu}
50062                 );
50063             }
50064             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
50065
50066             this.grid.on("headercontextmenu", this.handleHdCtx, this);
50067         }
50068
50069         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
50070             this.dd = new Roo.grid.GridDragZone(this.grid, {
50071                 ddGroup : this.grid.ddGroup || 'GridDD'
50072             });
50073         }
50074
50075         /*
50076         for(var i = 0; i < colCount; i++){
50077             if(cm.isHidden(i)){
50078                 this.hideColumn(i);
50079             }
50080             if(cm.config[i].align){
50081                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
50082                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
50083             }
50084         }*/
50085         
50086         this.updateHeaderSortState();
50087
50088         this.beforeInitialResize();
50089         this.layout(true);
50090
50091         // two part rendering gives faster view to the user
50092         this.renderPhase2.defer(1, this);
50093     },
50094
50095     renderPhase2 : function(){
50096         // render the rows now
50097         this.refresh();
50098         if(this.grid.autoSizeColumns){
50099             this.autoSizeColumns();
50100         }
50101     },
50102
50103     beforeInitialResize : function(){
50104
50105     },
50106
50107     onColumnSplitterMoved : function(i, w){
50108         this.userResized = true;
50109         var cm = this.grid.colModel;
50110         cm.setColumnWidth(i, w, true);
50111         var cid = cm.getColumnId(i);
50112         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50113         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50114         this.updateSplitters();
50115         this.layout();
50116         this.grid.fireEvent("columnresize", i, w);
50117     },
50118
50119     syncRowHeights : function(startIndex, endIndex){
50120         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
50121             startIndex = startIndex || 0;
50122             var mrows = this.getBodyTable().rows;
50123             var lrows = this.getLockedTable().rows;
50124             var len = mrows.length-1;
50125             endIndex = Math.min(endIndex || len, len);
50126             for(var i = startIndex; i <= endIndex; i++){
50127                 var m = mrows[i], l = lrows[i];
50128                 var h = Math.max(m.offsetHeight, l.offsetHeight);
50129                 m.style.height = l.style.height = h + "px";
50130             }
50131         }
50132     },
50133
50134     layout : function(initialRender, is2ndPass){
50135         var g = this.grid;
50136         var auto = g.autoHeight;
50137         var scrollOffset = 16;
50138         var c = g.getGridEl(), cm = this.cm,
50139                 expandCol = g.autoExpandColumn,
50140                 gv = this;
50141         //c.beginMeasure();
50142
50143         if(!c.dom.offsetWidth){ // display:none?
50144             if(initialRender){
50145                 this.lockedWrap.show();
50146                 this.mainWrap.show();
50147             }
50148             return;
50149         }
50150
50151         var hasLock = this.cm.isLocked(0);
50152
50153         var tbh = this.headerPanel.getHeight();
50154         var bbh = this.footerPanel.getHeight();
50155
50156         if(auto){
50157             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
50158             var newHeight = ch + c.getBorderWidth("tb");
50159             if(g.maxHeight){
50160                 newHeight = Math.min(g.maxHeight, newHeight);
50161             }
50162             c.setHeight(newHeight);
50163         }
50164
50165         if(g.autoWidth){
50166             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
50167         }
50168
50169         var s = this.scroller;
50170
50171         var csize = c.getSize(true);
50172
50173         this.el.setSize(csize.width, csize.height);
50174
50175         this.headerPanel.setWidth(csize.width);
50176         this.footerPanel.setWidth(csize.width);
50177
50178         var hdHeight = this.mainHd.getHeight();
50179         var vw = csize.width;
50180         var vh = csize.height - (tbh + bbh);
50181
50182         s.setSize(vw, vh);
50183
50184         var bt = this.getBodyTable();
50185         var ltWidth = hasLock ?
50186                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50187
50188         var scrollHeight = bt.offsetHeight;
50189         var scrollWidth = ltWidth + bt.offsetWidth;
50190         var vscroll = false, hscroll = false;
50191
50192         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50193
50194         var lw = this.lockedWrap, mw = this.mainWrap;
50195         var lb = this.lockedBody, mb = this.mainBody;
50196
50197         setTimeout(function(){
50198             var t = s.dom.offsetTop;
50199             var w = s.dom.clientWidth,
50200                 h = s.dom.clientHeight;
50201
50202             lw.setTop(t);
50203             lw.setSize(ltWidth, h);
50204
50205             mw.setLeftTop(ltWidth, t);
50206             mw.setSize(w-ltWidth, h);
50207
50208             lb.setHeight(h-hdHeight);
50209             mb.setHeight(h-hdHeight);
50210
50211             if(is2ndPass !== true && !gv.userResized && expandCol){
50212                 // high speed resize without full column calculation
50213                 
50214                 var ci = cm.getIndexById(expandCol);
50215                 if (ci < 0) {
50216                     ci = cm.findColumnIndex(expandCol);
50217                 }
50218                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50219                 var expandId = cm.getColumnId(ci);
50220                 var  tw = cm.getTotalWidth(false);
50221                 var currentWidth = cm.getColumnWidth(ci);
50222                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50223                 if(currentWidth != cw){
50224                     cm.setColumnWidth(ci, cw, true);
50225                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50226                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50227                     gv.updateSplitters();
50228                     gv.layout(false, true);
50229                 }
50230             }
50231
50232             if(initialRender){
50233                 lw.show();
50234                 mw.show();
50235             }
50236             //c.endMeasure();
50237         }, 10);
50238     },
50239
50240     onWindowResize : function(){
50241         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50242             return;
50243         }
50244         this.layout();
50245     },
50246
50247     appendFooter : function(parentEl){
50248         return null;
50249     },
50250
50251     sortAscText : "Sort Ascending",
50252     sortDescText : "Sort Descending",
50253     lockText : "Lock Column",
50254     unlockText : "Unlock Column",
50255     columnsText : "Columns"
50256 });
50257
50258
50259 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50260     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50261     this.proxy.el.addClass('x-grid3-col-dd');
50262 };
50263
50264 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50265     handleMouseDown : function(e){
50266
50267     },
50268
50269     callHandleMouseDown : function(e){
50270         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50271     }
50272 });
50273 /*
50274  * Based on:
50275  * Ext JS Library 1.1.1
50276  * Copyright(c) 2006-2007, Ext JS, LLC.
50277  *
50278  * Originally Released Under LGPL - original licence link has changed is not relivant.
50279  *
50280  * Fork - LGPL
50281  * <script type="text/javascript">
50282  */
50283  
50284 // private
50285 // This is a support class used internally by the Grid components
50286 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50287     this.grid = grid;
50288     this.view = grid.getView();
50289     this.proxy = this.view.resizeProxy;
50290     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50291         "gridSplitters" + this.grid.getGridEl().id, {
50292         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50293     });
50294     this.setHandleElId(Roo.id(hd));
50295     this.setOuterHandleElId(Roo.id(hd2));
50296     this.scroll = false;
50297 };
50298 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50299     fly: Roo.Element.fly,
50300
50301     b4StartDrag : function(x, y){
50302         this.view.headersDisabled = true;
50303         this.proxy.setHeight(this.view.mainWrap.getHeight());
50304         var w = this.cm.getColumnWidth(this.cellIndex);
50305         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50306         this.resetConstraints();
50307         this.setXConstraint(minw, 1000);
50308         this.setYConstraint(0, 0);
50309         this.minX = x - minw;
50310         this.maxX = x + 1000;
50311         this.startPos = x;
50312         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50313     },
50314
50315
50316     handleMouseDown : function(e){
50317         ev = Roo.EventObject.setEvent(e);
50318         var t = this.fly(ev.getTarget());
50319         if(t.hasClass("x-grid-split")){
50320             this.cellIndex = this.view.getCellIndex(t.dom);
50321             this.split = t.dom;
50322             this.cm = this.grid.colModel;
50323             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50324                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50325             }
50326         }
50327     },
50328
50329     endDrag : function(e){
50330         this.view.headersDisabled = false;
50331         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50332         var diff = endX - this.startPos;
50333         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50334     },
50335
50336     autoOffset : function(){
50337         this.setDelta(0,0);
50338     }
50339 });/*
50340  * Based on:
50341  * Ext JS Library 1.1.1
50342  * Copyright(c) 2006-2007, Ext JS, LLC.
50343  *
50344  * Originally Released Under LGPL - original licence link has changed is not relivant.
50345  *
50346  * Fork - LGPL
50347  * <script type="text/javascript">
50348  */
50349  
50350 // private
50351 // This is a support class used internally by the Grid components
50352 Roo.grid.GridDragZone = function(grid, config){
50353     this.view = grid.getView();
50354     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50355     if(this.view.lockedBody){
50356         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50357         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50358     }
50359     this.scroll = false;
50360     this.grid = grid;
50361     this.ddel = document.createElement('div');
50362     this.ddel.className = 'x-grid-dd-wrap';
50363 };
50364
50365 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50366     ddGroup : "GridDD",
50367
50368     getDragData : function(e){
50369         var t = Roo.lib.Event.getTarget(e);
50370         var rowIndex = this.view.findRowIndex(t);
50371         if(rowIndex !== false){
50372             var sm = this.grid.selModel;
50373             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50374               //  sm.mouseDown(e, t);
50375             //}
50376             if (e.hasModifier()){
50377                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50378             }
50379             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50380         }
50381         return false;
50382     },
50383
50384     onInitDrag : function(e){
50385         var data = this.dragData;
50386         this.ddel.innerHTML = this.grid.getDragDropText();
50387         this.proxy.update(this.ddel);
50388         // fire start drag?
50389     },
50390
50391     afterRepair : function(){
50392         this.dragging = false;
50393     },
50394
50395     getRepairXY : function(e, data){
50396         return false;
50397     },
50398
50399     onEndDrag : function(data, e){
50400         // fire end drag?
50401     },
50402
50403     onValidDrop : function(dd, e, id){
50404         // fire drag drop?
50405         this.hideProxy();
50406     },
50407
50408     beforeInvalidDrop : function(e, id){
50409
50410     }
50411 });/*
50412  * Based on:
50413  * Ext JS Library 1.1.1
50414  * Copyright(c) 2006-2007, Ext JS, LLC.
50415  *
50416  * Originally Released Under LGPL - original licence link has changed is not relivant.
50417  *
50418  * Fork - LGPL
50419  * <script type="text/javascript">
50420  */
50421  
50422
50423 /**
50424  * @class Roo.grid.ColumnModel
50425  * @extends Roo.util.Observable
50426  * This is the default implementation of a ColumnModel used by the Grid. It defines
50427  * the columns in the grid.
50428  * <br>Usage:<br>
50429  <pre><code>
50430  var colModel = new Roo.grid.ColumnModel([
50431         {header: "Ticker", width: 60, sortable: true, locked: true},
50432         {header: "Company Name", width: 150, sortable: true},
50433         {header: "Market Cap.", width: 100, sortable: true},
50434         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50435         {header: "Employees", width: 100, sortable: true, resizable: false}
50436  ]);
50437  </code></pre>
50438  * <p>
50439  
50440  * The config options listed for this class are options which may appear in each
50441  * individual column definition.
50442  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50443  * @constructor
50444  * @param {Object} config An Array of column config objects. See this class's
50445  * config objects for details.
50446 */
50447 Roo.grid.ColumnModel = function(config){
50448         /**
50449      * The config passed into the constructor
50450      */
50451     this.config = config;
50452     this.lookup = {};
50453
50454     // if no id, create one
50455     // if the column does not have a dataIndex mapping,
50456     // map it to the order it is in the config
50457     for(var i = 0, len = config.length; i < len; i++){
50458         var c = config[i];
50459         if(typeof c.dataIndex == "undefined"){
50460             c.dataIndex = i;
50461         }
50462         if(typeof c.renderer == "string"){
50463             c.renderer = Roo.util.Format[c.renderer];
50464         }
50465         if(typeof c.id == "undefined"){
50466             c.id = Roo.id();
50467         }
50468         if(c.editor && c.editor.xtype){
50469             c.editor  = Roo.factory(c.editor, Roo.grid);
50470         }
50471         if(c.editor && c.editor.isFormField){
50472             c.editor = new Roo.grid.GridEditor(c.editor);
50473         }
50474         this.lookup[c.id] = c;
50475     }
50476
50477     /**
50478      * The width of columns which have no width specified (defaults to 100)
50479      * @type Number
50480      */
50481     this.defaultWidth = 100;
50482
50483     /**
50484      * Default sortable of columns which have no sortable specified (defaults to false)
50485      * @type Boolean
50486      */
50487     this.defaultSortable = false;
50488
50489     this.addEvents({
50490         /**
50491              * @event widthchange
50492              * Fires when the width of a column changes.
50493              * @param {ColumnModel} this
50494              * @param {Number} columnIndex The column index
50495              * @param {Number} newWidth The new width
50496              */
50497             "widthchange": true,
50498         /**
50499              * @event headerchange
50500              * Fires when the text of a header changes.
50501              * @param {ColumnModel} this
50502              * @param {Number} columnIndex The column index
50503              * @param {Number} newText The new header text
50504              */
50505             "headerchange": true,
50506         /**
50507              * @event hiddenchange
50508              * Fires when a column is hidden or "unhidden".
50509              * @param {ColumnModel} this
50510              * @param {Number} columnIndex The column index
50511              * @param {Boolean} hidden true if hidden, false otherwise
50512              */
50513             "hiddenchange": true,
50514             /**
50515          * @event columnmoved
50516          * Fires when a column is moved.
50517          * @param {ColumnModel} this
50518          * @param {Number} oldIndex
50519          * @param {Number} newIndex
50520          */
50521         "columnmoved" : true,
50522         /**
50523          * @event columlockchange
50524          * Fires when a column's locked state is changed
50525          * @param {ColumnModel} this
50526          * @param {Number} colIndex
50527          * @param {Boolean} locked true if locked
50528          */
50529         "columnlockchange" : true
50530     });
50531     Roo.grid.ColumnModel.superclass.constructor.call(this);
50532 };
50533 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50534     /**
50535      * @cfg {String} header The header text to display in the Grid view.
50536      */
50537     /**
50538      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50539      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50540      * specified, the column's index is used as an index into the Record's data Array.
50541      */
50542     /**
50543      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50544      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50545      */
50546     /**
50547      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50548      * Defaults to the value of the {@link #defaultSortable} property.
50549      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50550      */
50551     /**
50552      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50553      */
50554     /**
50555      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50556      */
50557     /**
50558      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50559      */
50560     /**
50561      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50562      */
50563     /**
50564      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50565      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50566      * default renderer uses the raw data value.
50567      */
50568        /**
50569      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50570      */
50571     /**
50572      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50573      */
50574
50575     /**
50576      * Returns the id of the column at the specified index.
50577      * @param {Number} index The column index
50578      * @return {String} the id
50579      */
50580     getColumnId : function(index){
50581         return this.config[index].id;
50582     },
50583
50584     /**
50585      * Returns the column for a specified id.
50586      * @param {String} id The column id
50587      * @return {Object} the column
50588      */
50589     getColumnById : function(id){
50590         return this.lookup[id];
50591     },
50592
50593     
50594     /**
50595      * Returns the column for a specified dataIndex.
50596      * @param {String} dataIndex The column dataIndex
50597      * @return {Object|Boolean} the column or false if not found
50598      */
50599     getColumnByDataIndex: function(dataIndex){
50600         var index = this.findColumnIndex(dataIndex);
50601         return index > -1 ? this.config[index] : false;
50602     },
50603     
50604     /**
50605      * Returns the index for a specified column id.
50606      * @param {String} id The column id
50607      * @return {Number} the index, or -1 if not found
50608      */
50609     getIndexById : function(id){
50610         for(var i = 0, len = this.config.length; i < len; i++){
50611             if(this.config[i].id == id){
50612                 return i;
50613             }
50614         }
50615         return -1;
50616     },
50617     
50618     /**
50619      * Returns the index for a specified column dataIndex.
50620      * @param {String} dataIndex The column dataIndex
50621      * @return {Number} the index, or -1 if not found
50622      */
50623     
50624     findColumnIndex : function(dataIndex){
50625         for(var i = 0, len = this.config.length; i < len; i++){
50626             if(this.config[i].dataIndex == dataIndex){
50627                 return i;
50628             }
50629         }
50630         return -1;
50631     },
50632     
50633     
50634     moveColumn : function(oldIndex, newIndex){
50635         var c = this.config[oldIndex];
50636         this.config.splice(oldIndex, 1);
50637         this.config.splice(newIndex, 0, c);
50638         this.dataMap = null;
50639         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50640     },
50641
50642     isLocked : function(colIndex){
50643         return this.config[colIndex].locked === true;
50644     },
50645
50646     setLocked : function(colIndex, value, suppressEvent){
50647         if(this.isLocked(colIndex) == value){
50648             return;
50649         }
50650         this.config[colIndex].locked = value;
50651         if(!suppressEvent){
50652             this.fireEvent("columnlockchange", this, colIndex, value);
50653         }
50654     },
50655
50656     getTotalLockedWidth : function(){
50657         var totalWidth = 0;
50658         for(var i = 0; i < this.config.length; i++){
50659             if(this.isLocked(i) && !this.isHidden(i)){
50660                 this.totalWidth += this.getColumnWidth(i);
50661             }
50662         }
50663         return totalWidth;
50664     },
50665
50666     getLockedCount : function(){
50667         for(var i = 0, len = this.config.length; i < len; i++){
50668             if(!this.isLocked(i)){
50669                 return i;
50670             }
50671         }
50672     },
50673
50674     /**
50675      * Returns the number of columns.
50676      * @return {Number}
50677      */
50678     getColumnCount : function(visibleOnly){
50679         if(visibleOnly === true){
50680             var c = 0;
50681             for(var i = 0, len = this.config.length; i < len; i++){
50682                 if(!this.isHidden(i)){
50683                     c++;
50684                 }
50685             }
50686             return c;
50687         }
50688         return this.config.length;
50689     },
50690
50691     /**
50692      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50693      * @param {Function} fn
50694      * @param {Object} scope (optional)
50695      * @return {Array} result
50696      */
50697     getColumnsBy : function(fn, scope){
50698         var r = [];
50699         for(var i = 0, len = this.config.length; i < len; i++){
50700             var c = this.config[i];
50701             if(fn.call(scope||this, c, i) === true){
50702                 r[r.length] = c;
50703             }
50704         }
50705         return r;
50706     },
50707
50708     /**
50709      * Returns true if the specified column is sortable.
50710      * @param {Number} col The column index
50711      * @return {Boolean}
50712      */
50713     isSortable : function(col){
50714         if(typeof this.config[col].sortable == "undefined"){
50715             return this.defaultSortable;
50716         }
50717         return this.config[col].sortable;
50718     },
50719
50720     /**
50721      * Returns the rendering (formatting) function defined for the column.
50722      * @param {Number} col The column index.
50723      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50724      */
50725     getRenderer : function(col){
50726         if(!this.config[col].renderer){
50727             return Roo.grid.ColumnModel.defaultRenderer;
50728         }
50729         return this.config[col].renderer;
50730     },
50731
50732     /**
50733      * Sets the rendering (formatting) function for a column.
50734      * @param {Number} col The column index
50735      * @param {Function} fn The function to use to process the cell's raw data
50736      * to return HTML markup for the grid view. The render function is called with
50737      * the following parameters:<ul>
50738      * <li>Data value.</li>
50739      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50740      * <li>css A CSS style string to apply to the table cell.</li>
50741      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50742      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50743      * <li>Row index</li>
50744      * <li>Column index</li>
50745      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50746      */
50747     setRenderer : function(col, fn){
50748         this.config[col].renderer = fn;
50749     },
50750
50751     /**
50752      * Returns the width for the specified column.
50753      * @param {Number} col The column index
50754      * @return {Number}
50755      */
50756     getColumnWidth : function(col){
50757         return this.config[col].width * 1 || this.defaultWidth;
50758     },
50759
50760     /**
50761      * Sets the width for a column.
50762      * @param {Number} col The column index
50763      * @param {Number} width The new width
50764      */
50765     setColumnWidth : function(col, width, suppressEvent){
50766         this.config[col].width = width;
50767         this.totalWidth = null;
50768         if(!suppressEvent){
50769              this.fireEvent("widthchange", this, col, width);
50770         }
50771     },
50772
50773     /**
50774      * Returns the total width of all columns.
50775      * @param {Boolean} includeHidden True to include hidden column widths
50776      * @return {Number}
50777      */
50778     getTotalWidth : function(includeHidden){
50779         if(!this.totalWidth){
50780             this.totalWidth = 0;
50781             for(var i = 0, len = this.config.length; i < len; i++){
50782                 if(includeHidden || !this.isHidden(i)){
50783                     this.totalWidth += this.getColumnWidth(i);
50784                 }
50785             }
50786         }
50787         return this.totalWidth;
50788     },
50789
50790     /**
50791      * Returns the header for the specified column.
50792      * @param {Number} col The column index
50793      * @return {String}
50794      */
50795     getColumnHeader : function(col){
50796         return this.config[col].header;
50797     },
50798
50799     /**
50800      * Sets the header for a column.
50801      * @param {Number} col The column index
50802      * @param {String} header The new header
50803      */
50804     setColumnHeader : function(col, header){
50805         this.config[col].header = header;
50806         this.fireEvent("headerchange", this, col, header);
50807     },
50808
50809     /**
50810      * Returns the tooltip for the specified column.
50811      * @param {Number} col The column index
50812      * @return {String}
50813      */
50814     getColumnTooltip : function(col){
50815             return this.config[col].tooltip;
50816     },
50817     /**
50818      * Sets the tooltip for a column.
50819      * @param {Number} col The column index
50820      * @param {String} tooltip The new tooltip
50821      */
50822     setColumnTooltip : function(col, tooltip){
50823             this.config[col].tooltip = tooltip;
50824     },
50825
50826     /**
50827      * Returns the dataIndex for the specified column.
50828      * @param {Number} col The column index
50829      * @return {Number}
50830      */
50831     getDataIndex : function(col){
50832         return this.config[col].dataIndex;
50833     },
50834
50835     /**
50836      * Sets the dataIndex for a column.
50837      * @param {Number} col The column index
50838      * @param {Number} dataIndex The new dataIndex
50839      */
50840     setDataIndex : function(col, dataIndex){
50841         this.config[col].dataIndex = dataIndex;
50842     },
50843
50844     
50845     
50846     /**
50847      * Returns true if the cell is editable.
50848      * @param {Number} colIndex The column index
50849      * @param {Number} rowIndex The row index
50850      * @return {Boolean}
50851      */
50852     isCellEditable : function(colIndex, rowIndex){
50853         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50854     },
50855
50856     /**
50857      * Returns the editor defined for the cell/column.
50858      * return false or null to disable editing.
50859      * @param {Number} colIndex The column index
50860      * @param {Number} rowIndex The row index
50861      * @return {Object}
50862      */
50863     getCellEditor : function(colIndex, rowIndex){
50864         return this.config[colIndex].editor;
50865     },
50866
50867     /**
50868      * Sets if a column is editable.
50869      * @param {Number} col The column index
50870      * @param {Boolean} editable True if the column is editable
50871      */
50872     setEditable : function(col, editable){
50873         this.config[col].editable = editable;
50874     },
50875
50876
50877     /**
50878      * Returns true if the column is hidden.
50879      * @param {Number} colIndex The column index
50880      * @return {Boolean}
50881      */
50882     isHidden : function(colIndex){
50883         return this.config[colIndex].hidden;
50884     },
50885
50886
50887     /**
50888      * Returns true if the column width cannot be changed
50889      */
50890     isFixed : function(colIndex){
50891         return this.config[colIndex].fixed;
50892     },
50893
50894     /**
50895      * Returns true if the column can be resized
50896      * @return {Boolean}
50897      */
50898     isResizable : function(colIndex){
50899         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50900     },
50901     /**
50902      * Sets if a column is hidden.
50903      * @param {Number} colIndex The column index
50904      * @param {Boolean} hidden True if the column is hidden
50905      */
50906     setHidden : function(colIndex, hidden){
50907         this.config[colIndex].hidden = hidden;
50908         this.totalWidth = null;
50909         this.fireEvent("hiddenchange", this, colIndex, hidden);
50910     },
50911
50912     /**
50913      * Sets the editor for a column.
50914      * @param {Number} col The column index
50915      * @param {Object} editor The editor object
50916      */
50917     setEditor : function(col, editor){
50918         this.config[col].editor = editor;
50919     }
50920 });
50921
50922 Roo.grid.ColumnModel.defaultRenderer = function(value){
50923         if(typeof value == "string" && value.length < 1){
50924             return "&#160;";
50925         }
50926         return value;
50927 };
50928
50929 // Alias for backwards compatibility
50930 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50931 /*
50932  * Based on:
50933  * Ext JS Library 1.1.1
50934  * Copyright(c) 2006-2007, Ext JS, LLC.
50935  *
50936  * Originally Released Under LGPL - original licence link has changed is not relivant.
50937  *
50938  * Fork - LGPL
50939  * <script type="text/javascript">
50940  */
50941
50942 /**
50943  * @class Roo.grid.AbstractSelectionModel
50944  * @extends Roo.util.Observable
50945  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50946  * implemented by descendant classes.  This class should not be directly instantiated.
50947  * @constructor
50948  */
50949 Roo.grid.AbstractSelectionModel = function(){
50950     this.locked = false;
50951     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50952 };
50953
50954 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50955     /** @ignore Called by the grid automatically. Do not call directly. */
50956     init : function(grid){
50957         this.grid = grid;
50958         this.initEvents();
50959     },
50960
50961     /**
50962      * Locks the selections.
50963      */
50964     lock : function(){
50965         this.locked = true;
50966     },
50967
50968     /**
50969      * Unlocks the selections.
50970      */
50971     unlock : function(){
50972         this.locked = false;
50973     },
50974
50975     /**
50976      * Returns true if the selections are locked.
50977      * @return {Boolean}
50978      */
50979     isLocked : function(){
50980         return this.locked;
50981     }
50982 });/*
50983  * Based on:
50984  * Ext JS Library 1.1.1
50985  * Copyright(c) 2006-2007, Ext JS, LLC.
50986  *
50987  * Originally Released Under LGPL - original licence link has changed is not relivant.
50988  *
50989  * Fork - LGPL
50990  * <script type="text/javascript">
50991  */
50992 /**
50993  * @extends Roo.grid.AbstractSelectionModel
50994  * @class Roo.grid.RowSelectionModel
50995  * The default SelectionModel used by {@link Roo.grid.Grid}.
50996  * It supports multiple selections and keyboard selection/navigation. 
50997  * @constructor
50998  * @param {Object} config
50999  */
51000 Roo.grid.RowSelectionModel = function(config){
51001     Roo.apply(this, config);
51002     this.selections = new Roo.util.MixedCollection(false, function(o){
51003         return o.id;
51004     });
51005
51006     this.last = false;
51007     this.lastActive = false;
51008
51009     this.addEvents({
51010         /**
51011              * @event selectionchange
51012              * Fires when the selection changes
51013              * @param {SelectionModel} this
51014              */
51015             "selectionchange" : true,
51016         /**
51017              * @event afterselectionchange
51018              * Fires after the selection changes (eg. by key press or clicking)
51019              * @param {SelectionModel} this
51020              */
51021             "afterselectionchange" : true,
51022         /**
51023              * @event beforerowselect
51024              * Fires when a row is selected being selected, return false to cancel.
51025              * @param {SelectionModel} this
51026              * @param {Number} rowIndex The selected index
51027              * @param {Boolean} keepExisting False if other selections will be cleared
51028              */
51029             "beforerowselect" : true,
51030         /**
51031              * @event rowselect
51032              * Fires when a row is selected.
51033              * @param {SelectionModel} this
51034              * @param {Number} rowIndex The selected index
51035              * @param {Roo.data.Record} r The record
51036              */
51037             "rowselect" : true,
51038         /**
51039              * @event rowdeselect
51040              * Fires when a row is deselected.
51041              * @param {SelectionModel} this
51042              * @param {Number} rowIndex The selected index
51043              */
51044         "rowdeselect" : true
51045     });
51046     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
51047     this.locked = false;
51048 };
51049
51050 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
51051     /**
51052      * @cfg {Boolean} singleSelect
51053      * True to allow selection of only one row at a time (defaults to false)
51054      */
51055     singleSelect : false,
51056
51057     // private
51058     initEvents : function(){
51059
51060         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
51061             this.grid.on("mousedown", this.handleMouseDown, this);
51062         }else{ // allow click to work like normal
51063             this.grid.on("rowclick", this.handleDragableRowClick, this);
51064         }
51065
51066         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
51067             "up" : function(e){
51068                 if(!e.shiftKey){
51069                     this.selectPrevious(e.shiftKey);
51070                 }else if(this.last !== false && this.lastActive !== false){
51071                     var last = this.last;
51072                     this.selectRange(this.last,  this.lastActive-1);
51073                     this.grid.getView().focusRow(this.lastActive);
51074                     if(last !== false){
51075                         this.last = last;
51076                     }
51077                 }else{
51078                     this.selectFirstRow();
51079                 }
51080                 this.fireEvent("afterselectionchange", this);
51081             },
51082             "down" : function(e){
51083                 if(!e.shiftKey){
51084                     this.selectNext(e.shiftKey);
51085                 }else if(this.last !== false && this.lastActive !== false){
51086                     var last = this.last;
51087                     this.selectRange(this.last,  this.lastActive+1);
51088                     this.grid.getView().focusRow(this.lastActive);
51089                     if(last !== false){
51090                         this.last = last;
51091                     }
51092                 }else{
51093                     this.selectFirstRow();
51094                 }
51095                 this.fireEvent("afterselectionchange", this);
51096             },
51097             scope: this
51098         });
51099
51100         var view = this.grid.view;
51101         view.on("refresh", this.onRefresh, this);
51102         view.on("rowupdated", this.onRowUpdated, this);
51103         view.on("rowremoved", this.onRemove, this);
51104     },
51105
51106     // private
51107     onRefresh : function(){
51108         var ds = this.grid.dataSource, i, v = this.grid.view;
51109         var s = this.selections;
51110         s.each(function(r){
51111             if((i = ds.indexOfId(r.id)) != -1){
51112                 v.onRowSelect(i);
51113             }else{
51114                 s.remove(r);
51115             }
51116         });
51117     },
51118
51119     // private
51120     onRemove : function(v, index, r){
51121         this.selections.remove(r);
51122     },
51123
51124     // private
51125     onRowUpdated : function(v, index, r){
51126         if(this.isSelected(r)){
51127             v.onRowSelect(index);
51128         }
51129     },
51130
51131     /**
51132      * Select records.
51133      * @param {Array} records The records to select
51134      * @param {Boolean} keepExisting (optional) True to keep existing selections
51135      */
51136     selectRecords : function(records, keepExisting){
51137         if(!keepExisting){
51138             this.clearSelections();
51139         }
51140         var ds = this.grid.dataSource;
51141         for(var i = 0, len = records.length; i < len; i++){
51142             this.selectRow(ds.indexOf(records[i]), true);
51143         }
51144     },
51145
51146     /**
51147      * Gets the number of selected rows.
51148      * @return {Number}
51149      */
51150     getCount : function(){
51151         return this.selections.length;
51152     },
51153
51154     /**
51155      * Selects the first row in the grid.
51156      */
51157     selectFirstRow : function(){
51158         this.selectRow(0);
51159     },
51160
51161     /**
51162      * Select the last row.
51163      * @param {Boolean} keepExisting (optional) True to keep existing selections
51164      */
51165     selectLastRow : function(keepExisting){
51166         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
51167     },
51168
51169     /**
51170      * Selects the row immediately following the last selected row.
51171      * @param {Boolean} keepExisting (optional) True to keep existing selections
51172      */
51173     selectNext : function(keepExisting){
51174         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
51175             this.selectRow(this.last+1, keepExisting);
51176             this.grid.getView().focusRow(this.last);
51177         }
51178     },
51179
51180     /**
51181      * Selects the row that precedes the last selected row.
51182      * @param {Boolean} keepExisting (optional) True to keep existing selections
51183      */
51184     selectPrevious : function(keepExisting){
51185         if(this.last){
51186             this.selectRow(this.last-1, keepExisting);
51187             this.grid.getView().focusRow(this.last);
51188         }
51189     },
51190
51191     /**
51192      * Returns the selected records
51193      * @return {Array} Array of selected records
51194      */
51195     getSelections : function(){
51196         return [].concat(this.selections.items);
51197     },
51198
51199     /**
51200      * Returns the first selected record.
51201      * @return {Record}
51202      */
51203     getSelected : function(){
51204         return this.selections.itemAt(0);
51205     },
51206
51207
51208     /**
51209      * Clears all selections.
51210      */
51211     clearSelections : function(fast){
51212         if(this.locked) return;
51213         if(fast !== true){
51214             var ds = this.grid.dataSource;
51215             var s = this.selections;
51216             s.each(function(r){
51217                 this.deselectRow(ds.indexOfId(r.id));
51218             }, this);
51219             s.clear();
51220         }else{
51221             this.selections.clear();
51222         }
51223         this.last = false;
51224     },
51225
51226
51227     /**
51228      * Selects all rows.
51229      */
51230     selectAll : function(){
51231         if(this.locked) return;
51232         this.selections.clear();
51233         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51234             this.selectRow(i, true);
51235         }
51236     },
51237
51238     /**
51239      * Returns True if there is a selection.
51240      * @return {Boolean}
51241      */
51242     hasSelection : function(){
51243         return this.selections.length > 0;
51244     },
51245
51246     /**
51247      * Returns True if the specified row is selected.
51248      * @param {Number/Record} record The record or index of the record to check
51249      * @return {Boolean}
51250      */
51251     isSelected : function(index){
51252         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51253         return (r && this.selections.key(r.id) ? true : false);
51254     },
51255
51256     /**
51257      * Returns True if the specified record id is selected.
51258      * @param {String} id The id of record to check
51259      * @return {Boolean}
51260      */
51261     isIdSelected : function(id){
51262         return (this.selections.key(id) ? true : false);
51263     },
51264
51265     // private
51266     handleMouseDown : function(e, t){
51267         var view = this.grid.getView(), rowIndex;
51268         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51269             return;
51270         };
51271         if(e.shiftKey && this.last !== false){
51272             var last = this.last;
51273             this.selectRange(last, rowIndex, e.ctrlKey);
51274             this.last = last; // reset the last
51275             view.focusRow(rowIndex);
51276         }else{
51277             var isSelected = this.isSelected(rowIndex);
51278             if(e.button !== 0 && isSelected){
51279                 view.focusRow(rowIndex);
51280             }else if(e.ctrlKey && isSelected){
51281                 this.deselectRow(rowIndex);
51282             }else if(!isSelected){
51283                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51284                 view.focusRow(rowIndex);
51285             }
51286         }
51287         this.fireEvent("afterselectionchange", this);
51288     },
51289     // private
51290     handleDragableRowClick :  function(grid, rowIndex, e) 
51291     {
51292         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51293             this.selectRow(rowIndex, false);
51294             grid.view.focusRow(rowIndex);
51295              this.fireEvent("afterselectionchange", this);
51296         }
51297     },
51298     
51299     /**
51300      * Selects multiple rows.
51301      * @param {Array} rows Array of the indexes of the row to select
51302      * @param {Boolean} keepExisting (optional) True to keep existing selections
51303      */
51304     selectRows : function(rows, keepExisting){
51305         if(!keepExisting){
51306             this.clearSelections();
51307         }
51308         for(var i = 0, len = rows.length; i < len; i++){
51309             this.selectRow(rows[i], true);
51310         }
51311     },
51312
51313     /**
51314      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51315      * @param {Number} startRow The index of the first row in the range
51316      * @param {Number} endRow The index of the last row in the range
51317      * @param {Boolean} keepExisting (optional) True to retain existing selections
51318      */
51319     selectRange : function(startRow, endRow, keepExisting){
51320         if(this.locked) return;
51321         if(!keepExisting){
51322             this.clearSelections();
51323         }
51324         if(startRow <= endRow){
51325             for(var i = startRow; i <= endRow; i++){
51326                 this.selectRow(i, true);
51327             }
51328         }else{
51329             for(var i = startRow; i >= endRow; i--){
51330                 this.selectRow(i, true);
51331             }
51332         }
51333     },
51334
51335     /**
51336      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51337      * @param {Number} startRow The index of the first row in the range
51338      * @param {Number} endRow The index of the last row in the range
51339      */
51340     deselectRange : function(startRow, endRow, preventViewNotify){
51341         if(this.locked) return;
51342         for(var i = startRow; i <= endRow; i++){
51343             this.deselectRow(i, preventViewNotify);
51344         }
51345     },
51346
51347     /**
51348      * Selects a row.
51349      * @param {Number} row The index of the row to select
51350      * @param {Boolean} keepExisting (optional) True to keep existing selections
51351      */
51352     selectRow : function(index, keepExisting, preventViewNotify){
51353         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51354         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51355             if(!keepExisting || this.singleSelect){
51356                 this.clearSelections();
51357             }
51358             var r = this.grid.dataSource.getAt(index);
51359             this.selections.add(r);
51360             this.last = this.lastActive = index;
51361             if(!preventViewNotify){
51362                 this.grid.getView().onRowSelect(index);
51363             }
51364             this.fireEvent("rowselect", this, index, r);
51365             this.fireEvent("selectionchange", this);
51366         }
51367     },
51368
51369     /**
51370      * Deselects a row.
51371      * @param {Number} row The index of the row to deselect
51372      */
51373     deselectRow : function(index, preventViewNotify){
51374         if(this.locked) return;
51375         if(this.last == index){
51376             this.last = false;
51377         }
51378         if(this.lastActive == index){
51379             this.lastActive = false;
51380         }
51381         var r = this.grid.dataSource.getAt(index);
51382         this.selections.remove(r);
51383         if(!preventViewNotify){
51384             this.grid.getView().onRowDeselect(index);
51385         }
51386         this.fireEvent("rowdeselect", this, index);
51387         this.fireEvent("selectionchange", this);
51388     },
51389
51390     // private
51391     restoreLast : function(){
51392         if(this._last){
51393             this.last = this._last;
51394         }
51395     },
51396
51397     // private
51398     acceptsNav : function(row, col, cm){
51399         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51400     },
51401
51402     // private
51403     onEditorKey : function(field, e){
51404         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51405         if(k == e.TAB){
51406             e.stopEvent();
51407             ed.completeEdit();
51408             if(e.shiftKey){
51409                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51410             }else{
51411                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51412             }
51413         }else if(k == e.ENTER && !e.ctrlKey){
51414             e.stopEvent();
51415             ed.completeEdit();
51416             if(e.shiftKey){
51417                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51418             }else{
51419                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51420             }
51421         }else if(k == e.ESC){
51422             ed.cancelEdit();
51423         }
51424         if(newCell){
51425             g.startEditing(newCell[0], newCell[1]);
51426         }
51427     }
51428 });/*
51429  * Based on:
51430  * Ext JS Library 1.1.1
51431  * Copyright(c) 2006-2007, Ext JS, LLC.
51432  *
51433  * Originally Released Under LGPL - original licence link has changed is not relivant.
51434  *
51435  * Fork - LGPL
51436  * <script type="text/javascript">
51437  */
51438 /**
51439  * @class Roo.grid.CellSelectionModel
51440  * @extends Roo.grid.AbstractSelectionModel
51441  * This class provides the basic implementation for cell selection in a grid.
51442  * @constructor
51443  * @param {Object} config The object containing the configuration of this model.
51444  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51445  */
51446 Roo.grid.CellSelectionModel = function(config){
51447     Roo.apply(this, config);
51448
51449     this.selection = null;
51450
51451     this.addEvents({
51452         /**
51453              * @event beforerowselect
51454              * Fires before a cell is selected.
51455              * @param {SelectionModel} this
51456              * @param {Number} rowIndex The selected row index
51457              * @param {Number} colIndex The selected cell index
51458              */
51459             "beforecellselect" : true,
51460         /**
51461              * @event cellselect
51462              * Fires when a cell is selected.
51463              * @param {SelectionModel} this
51464              * @param {Number} rowIndex The selected row index
51465              * @param {Number} colIndex The selected cell index
51466              */
51467             "cellselect" : true,
51468         /**
51469              * @event selectionchange
51470              * Fires when the active selection changes.
51471              * @param {SelectionModel} this
51472              * @param {Object} selection null for no selection or an object (o) with two properties
51473                 <ul>
51474                 <li>o.record: the record object for the row the selection is in</li>
51475                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51476                 </ul>
51477              */
51478             "selectionchange" : true,
51479         /**
51480              * @event tabend
51481              * Fires when the tab (or enter) was pressed on the last editable cell
51482              * You can use this to trigger add new row.
51483              * @param {SelectionModel} this
51484              */
51485             "tabend" : true,
51486          /**
51487              * @event beforeeditnext
51488              * Fires before the next editable sell is made active
51489              * You can use this to skip to another cell or fire the tabend
51490              *    if you set cell to false
51491              * @param {Object} eventdata object : { cell : [ row, col ] } 
51492              */
51493             "beforeeditnext" : true
51494     });
51495     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51496 };
51497
51498 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51499     
51500     enter_is_tab: false,
51501
51502     /** @ignore */
51503     initEvents : function(){
51504         this.grid.on("mousedown", this.handleMouseDown, this);
51505         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51506         var view = this.grid.view;
51507         view.on("refresh", this.onViewChange, this);
51508         view.on("rowupdated", this.onRowUpdated, this);
51509         view.on("beforerowremoved", this.clearSelections, this);
51510         view.on("beforerowsinserted", this.clearSelections, this);
51511         if(this.grid.isEditor){
51512             this.grid.on("beforeedit", this.beforeEdit,  this);
51513         }
51514     },
51515
51516         //private
51517     beforeEdit : function(e){
51518         this.select(e.row, e.column, false, true, e.record);
51519     },
51520
51521         //private
51522     onRowUpdated : function(v, index, r){
51523         if(this.selection && this.selection.record == r){
51524             v.onCellSelect(index, this.selection.cell[1]);
51525         }
51526     },
51527
51528         //private
51529     onViewChange : function(){
51530         this.clearSelections(true);
51531     },
51532
51533         /**
51534          * Returns the currently selected cell,.
51535          * @return {Array} The selected cell (row, column) or null if none selected.
51536          */
51537     getSelectedCell : function(){
51538         return this.selection ? this.selection.cell : null;
51539     },
51540
51541     /**
51542      * Clears all selections.
51543      * @param {Boolean} true to prevent the gridview from being notified about the change.
51544      */
51545     clearSelections : function(preventNotify){
51546         var s = this.selection;
51547         if(s){
51548             if(preventNotify !== true){
51549                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51550             }
51551             this.selection = null;
51552             this.fireEvent("selectionchange", this, null);
51553         }
51554     },
51555
51556     /**
51557      * Returns true if there is a selection.
51558      * @return {Boolean}
51559      */
51560     hasSelection : function(){
51561         return this.selection ? true : false;
51562     },
51563
51564     /** @ignore */
51565     handleMouseDown : function(e, t){
51566         var v = this.grid.getView();
51567         if(this.isLocked()){
51568             return;
51569         };
51570         var row = v.findRowIndex(t);
51571         var cell = v.findCellIndex(t);
51572         if(row !== false && cell !== false){
51573             this.select(row, cell);
51574         }
51575     },
51576
51577     /**
51578      * Selects a cell.
51579      * @param {Number} rowIndex
51580      * @param {Number} collIndex
51581      */
51582     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51583         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51584             this.clearSelections();
51585             r = r || this.grid.dataSource.getAt(rowIndex);
51586             this.selection = {
51587                 record : r,
51588                 cell : [rowIndex, colIndex]
51589             };
51590             if(!preventViewNotify){
51591                 var v = this.grid.getView();
51592                 v.onCellSelect(rowIndex, colIndex);
51593                 if(preventFocus !== true){
51594                     v.focusCell(rowIndex, colIndex);
51595                 }
51596             }
51597             this.fireEvent("cellselect", this, rowIndex, colIndex);
51598             this.fireEvent("selectionchange", this, this.selection);
51599         }
51600     },
51601
51602         //private
51603     isSelectable : function(rowIndex, colIndex, cm){
51604         return !cm.isHidden(colIndex);
51605     },
51606
51607     /** @ignore */
51608     handleKeyDown : function(e){
51609         //Roo.log('Cell Sel Model handleKeyDown');
51610         if(!e.isNavKeyPress()){
51611             return;
51612         }
51613         var g = this.grid, s = this.selection;
51614         if(!s){
51615             e.stopEvent();
51616             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51617             if(cell){
51618                 this.select(cell[0], cell[1]);
51619             }
51620             return;
51621         }
51622         var sm = this;
51623         var walk = function(row, col, step){
51624             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51625         };
51626         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51627         var newCell;
51628
51629       
51630
51631         switch(k){
51632             case e.TAB:
51633                 // handled by onEditorKey
51634                 if (g.isEditor && g.editing) {
51635                     return;
51636                 }
51637                 if(e.shiftKey) {
51638                     newCell = walk(r, c-1, -1);
51639                 } else {
51640                     newCell = walk(r, c+1, 1);
51641                 }
51642                 break;
51643             
51644             case e.DOWN:
51645                newCell = walk(r+1, c, 1);
51646                 break;
51647             
51648             case e.UP:
51649                 newCell = walk(r-1, c, -1);
51650                 break;
51651             
51652             case e.RIGHT:
51653                 newCell = walk(r, c+1, 1);
51654                 break;
51655             
51656             case e.LEFT:
51657                 newCell = walk(r, c-1, -1);
51658                 break;
51659             
51660             case e.ENTER:
51661                 
51662                 if(g.isEditor && !g.editing){
51663                    g.startEditing(r, c);
51664                    e.stopEvent();
51665                    return;
51666                 }
51667                 
51668                 
51669              break;
51670         };
51671         if(newCell){
51672             this.select(newCell[0], newCell[1]);
51673             e.stopEvent();
51674             
51675         }
51676     },
51677
51678     acceptsNav : function(row, col, cm){
51679         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51680     },
51681     /**
51682      * Selects a cell.
51683      * @param {Number} field (not used) - as it's normally used as a listener
51684      * @param {Number} e - event - fake it by using
51685      *
51686      * var e = Roo.EventObjectImpl.prototype;
51687      * e.keyCode = e.TAB
51688      *
51689      * 
51690      */
51691     onEditorKey : function(field, e){
51692         
51693         var k = e.getKey(),
51694             newCell,
51695             g = this.grid,
51696             ed = g.activeEditor,
51697             forward = false;
51698         ///Roo.log('onEditorKey' + k);
51699         
51700         
51701         if (this.enter_is_tab && k == e.ENTER) {
51702             k = e.TAB;
51703         }
51704         
51705         if(k == e.TAB){
51706             if(e.shiftKey){
51707                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51708             }else{
51709                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51710                 forward = true;
51711             }
51712             
51713             e.stopEvent();
51714             
51715         } else if(k == e.ENTER &&  !e.ctrlKey){
51716             ed.completeEdit();
51717             e.stopEvent();
51718             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51719         
51720                 } else if(k == e.ESC){
51721             ed.cancelEdit();
51722         }
51723                 
51724         if (newCell) {
51725             var ecall = { cell : newCell, forward : forward };
51726             this.fireEvent('beforeeditnext', ecall );
51727             newCell = ecall.cell;
51728                         forward = ecall.forward;
51729         }
51730                 
51731         if(newCell){
51732             //Roo.log('next cell after edit');
51733             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51734         } else if (forward) {
51735             // tabbed past last
51736             this.fireEvent.defer(100, this, ['tabend',this]);
51737         }
51738     }
51739 });/*
51740  * Based on:
51741  * Ext JS Library 1.1.1
51742  * Copyright(c) 2006-2007, Ext JS, LLC.
51743  *
51744  * Originally Released Under LGPL - original licence link has changed is not relivant.
51745  *
51746  * Fork - LGPL
51747  * <script type="text/javascript">
51748  */
51749  
51750 /**
51751  * @class Roo.grid.EditorGrid
51752  * @extends Roo.grid.Grid
51753  * Class for creating and editable grid.
51754  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51755  * The container MUST have some type of size defined for the grid to fill. The container will be 
51756  * automatically set to position relative if it isn't already.
51757  * @param {Object} dataSource The data model to bind to
51758  * @param {Object} colModel The column model with info about this grid's columns
51759  */
51760 Roo.grid.EditorGrid = function(container, config){
51761     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51762     this.getGridEl().addClass("xedit-grid");
51763
51764     if(!this.selModel){
51765         this.selModel = new Roo.grid.CellSelectionModel();
51766     }
51767
51768     this.activeEditor = null;
51769
51770         this.addEvents({
51771             /**
51772              * @event beforeedit
51773              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51774              * <ul style="padding:5px;padding-left:16px;">
51775              * <li>grid - This grid</li>
51776              * <li>record - The record being edited</li>
51777              * <li>field - The field name being edited</li>
51778              * <li>value - The value for the field being edited.</li>
51779              * <li>row - The grid row index</li>
51780              * <li>column - The grid column index</li>
51781              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51782              * </ul>
51783              * @param {Object} e An edit event (see above for description)
51784              */
51785             "beforeedit" : true,
51786             /**
51787              * @event afteredit
51788              * Fires after a cell is edited. <br />
51789              * <ul style="padding:5px;padding-left:16px;">
51790              * <li>grid - This grid</li>
51791              * <li>record - The record being edited</li>
51792              * <li>field - The field name being edited</li>
51793              * <li>value - The value being set</li>
51794              * <li>originalValue - The original value for the field, before the edit.</li>
51795              * <li>row - The grid row index</li>
51796              * <li>column - The grid column index</li>
51797              * </ul>
51798              * @param {Object} e An edit event (see above for description)
51799              */
51800             "afteredit" : true,
51801             /**
51802              * @event validateedit
51803              * Fires after a cell is edited, but before the value is set in the record. 
51804          * You can use this to modify the value being set in the field, Return false
51805              * to cancel the change. The edit event object has the following properties <br />
51806              * <ul style="padding:5px;padding-left:16px;">
51807          * <li>editor - This editor</li>
51808              * <li>grid - This grid</li>
51809              * <li>record - The record being edited</li>
51810              * <li>field - The field name being edited</li>
51811              * <li>value - The value being set</li>
51812              * <li>originalValue - The original value for the field, before the edit.</li>
51813              * <li>row - The grid row index</li>
51814              * <li>column - The grid column index</li>
51815              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51816              * </ul>
51817              * @param {Object} e An edit event (see above for description)
51818              */
51819             "validateedit" : true
51820         });
51821     this.on("bodyscroll", this.stopEditing,  this);
51822     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51823 };
51824
51825 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51826     /**
51827      * @cfg {Number} clicksToEdit
51828      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51829      */
51830     clicksToEdit: 2,
51831
51832     // private
51833     isEditor : true,
51834     // private
51835     trackMouseOver: false, // causes very odd FF errors
51836
51837     onCellDblClick : function(g, row, col){
51838         this.startEditing(row, col);
51839     },
51840
51841     onEditComplete : function(ed, value, startValue){
51842         this.editing = false;
51843         this.activeEditor = null;
51844         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51845         var r = ed.record;
51846         var field = this.colModel.getDataIndex(ed.col);
51847         var e = {
51848             grid: this,
51849             record: r,
51850             field: field,
51851             originalValue: startValue,
51852             value: value,
51853             row: ed.row,
51854             column: ed.col,
51855             cancel:false,
51856             editor: ed
51857         };
51858         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51859         cell.show();
51860           
51861         if(String(value) !== String(startValue)){
51862             
51863             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51864                 r.set(field, e.value);
51865                 // if we are dealing with a combo box..
51866                 // then we also set the 'name' colum to be the displayField
51867                 if (ed.field.displayField && ed.field.name) {
51868                     r.set(ed.field.name, ed.field.el.dom.value);
51869                 }
51870                 
51871                 delete e.cancel; //?? why!!!
51872                 this.fireEvent("afteredit", e);
51873             }
51874         } else {
51875             this.fireEvent("afteredit", e); // always fire it!
51876         }
51877         this.view.focusCell(ed.row, ed.col);
51878     },
51879
51880     /**
51881      * Starts editing the specified for the specified row/column
51882      * @param {Number} rowIndex
51883      * @param {Number} colIndex
51884      */
51885     startEditing : function(row, col){
51886         this.stopEditing();
51887         if(this.colModel.isCellEditable(col, row)){
51888             this.view.ensureVisible(row, col, true);
51889           
51890             var r = this.dataSource.getAt(row);
51891             var field = this.colModel.getDataIndex(col);
51892             var cell = Roo.get(this.view.getCell(row,col));
51893             var e = {
51894                 grid: this,
51895                 record: r,
51896                 field: field,
51897                 value: r.data[field],
51898                 row: row,
51899                 column: col,
51900                 cancel:false 
51901             };
51902             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51903                 this.editing = true;
51904                 var ed = this.colModel.getCellEditor(col, row);
51905                 
51906                 if (!ed) {
51907                     return;
51908                 }
51909                 if(!ed.rendered){
51910                     ed.render(ed.parentEl || document.body);
51911                 }
51912                 ed.field.reset();
51913                
51914                 cell.hide();
51915                 
51916                 (function(){ // complex but required for focus issues in safari, ie and opera
51917                     ed.row = row;
51918                     ed.col = col;
51919                     ed.record = r;
51920                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51921                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51922                     this.activeEditor = ed;
51923                     var v = r.data[field];
51924                     ed.startEdit(this.view.getCell(row, col), v);
51925                     // combo's with 'displayField and name set
51926                     if (ed.field.displayField && ed.field.name) {
51927                         ed.field.el.dom.value = r.data[ed.field.name];
51928                     }
51929                     
51930                     
51931                 }).defer(50, this);
51932             }
51933         }
51934     },
51935         
51936     /**
51937      * Stops any active editing
51938      */
51939     stopEditing : function(){
51940         if(this.activeEditor){
51941             this.activeEditor.completeEdit();
51942         }
51943         this.activeEditor = null;
51944     }
51945 });/*
51946  * Based on:
51947  * Ext JS Library 1.1.1
51948  * Copyright(c) 2006-2007, Ext JS, LLC.
51949  *
51950  * Originally Released Under LGPL - original licence link has changed is not relivant.
51951  *
51952  * Fork - LGPL
51953  * <script type="text/javascript">
51954  */
51955
51956 // private - not really -- you end up using it !
51957 // This is a support class used internally by the Grid components
51958
51959 /**
51960  * @class Roo.grid.GridEditor
51961  * @extends Roo.Editor
51962  * Class for creating and editable grid elements.
51963  * @param {Object} config any settings (must include field)
51964  */
51965 Roo.grid.GridEditor = function(field, config){
51966     if (!config && field.field) {
51967         config = field;
51968         field = Roo.factory(config.field, Roo.form);
51969     }
51970     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51971     field.monitorTab = false;
51972 };
51973
51974 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51975     
51976     /**
51977      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51978      */
51979     
51980     alignment: "tl-tl",
51981     autoSize: "width",
51982     hideEl : false,
51983     cls: "x-small-editor x-grid-editor",
51984     shim:false,
51985     shadow:"frame"
51986 });/*
51987  * Based on:
51988  * Ext JS Library 1.1.1
51989  * Copyright(c) 2006-2007, Ext JS, LLC.
51990  *
51991  * Originally Released Under LGPL - original licence link has changed is not relivant.
51992  *
51993  * Fork - LGPL
51994  * <script type="text/javascript">
51995  */
51996   
51997
51998   
51999 Roo.grid.PropertyRecord = Roo.data.Record.create([
52000     {name:'name',type:'string'},  'value'
52001 ]);
52002
52003
52004 Roo.grid.PropertyStore = function(grid, source){
52005     this.grid = grid;
52006     this.store = new Roo.data.Store({
52007         recordType : Roo.grid.PropertyRecord
52008     });
52009     this.store.on('update', this.onUpdate,  this);
52010     if(source){
52011         this.setSource(source);
52012     }
52013     Roo.grid.PropertyStore.superclass.constructor.call(this);
52014 };
52015
52016
52017
52018 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
52019     setSource : function(o){
52020         this.source = o;
52021         this.store.removeAll();
52022         var data = [];
52023         for(var k in o){
52024             if(this.isEditableValue(o[k])){
52025                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
52026             }
52027         }
52028         this.store.loadRecords({records: data}, {}, true);
52029     },
52030
52031     onUpdate : function(ds, record, type){
52032         if(type == Roo.data.Record.EDIT){
52033             var v = record.data['value'];
52034             var oldValue = record.modified['value'];
52035             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
52036                 this.source[record.id] = v;
52037                 record.commit();
52038                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
52039             }else{
52040                 record.reject();
52041             }
52042         }
52043     },
52044
52045     getProperty : function(row){
52046        return this.store.getAt(row);
52047     },
52048
52049     isEditableValue: function(val){
52050         if(val && val instanceof Date){
52051             return true;
52052         }else if(typeof val == 'object' || typeof val == 'function'){
52053             return false;
52054         }
52055         return true;
52056     },
52057
52058     setValue : function(prop, value){
52059         this.source[prop] = value;
52060         this.store.getById(prop).set('value', value);
52061     },
52062
52063     getSource : function(){
52064         return this.source;
52065     }
52066 });
52067
52068 Roo.grid.PropertyColumnModel = function(grid, store){
52069     this.grid = grid;
52070     var g = Roo.grid;
52071     g.PropertyColumnModel.superclass.constructor.call(this, [
52072         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
52073         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
52074     ]);
52075     this.store = store;
52076     this.bselect = Roo.DomHelper.append(document.body, {
52077         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
52078             {tag: 'option', value: 'true', html: 'true'},
52079             {tag: 'option', value: 'false', html: 'false'}
52080         ]
52081     });
52082     Roo.id(this.bselect);
52083     var f = Roo.form;
52084     this.editors = {
52085         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
52086         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
52087         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
52088         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
52089         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
52090     };
52091     this.renderCellDelegate = this.renderCell.createDelegate(this);
52092     this.renderPropDelegate = this.renderProp.createDelegate(this);
52093 };
52094
52095 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
52096     
52097     
52098     nameText : 'Name',
52099     valueText : 'Value',
52100     
52101     dateFormat : 'm/j/Y',
52102     
52103     
52104     renderDate : function(dateVal){
52105         return dateVal.dateFormat(this.dateFormat);
52106     },
52107
52108     renderBool : function(bVal){
52109         return bVal ? 'true' : 'false';
52110     },
52111
52112     isCellEditable : function(colIndex, rowIndex){
52113         return colIndex == 1;
52114     },
52115
52116     getRenderer : function(col){
52117         return col == 1 ?
52118             this.renderCellDelegate : this.renderPropDelegate;
52119     },
52120
52121     renderProp : function(v){
52122         return this.getPropertyName(v);
52123     },
52124
52125     renderCell : function(val){
52126         var rv = val;
52127         if(val instanceof Date){
52128             rv = this.renderDate(val);
52129         }else if(typeof val == 'boolean'){
52130             rv = this.renderBool(val);
52131         }
52132         return Roo.util.Format.htmlEncode(rv);
52133     },
52134
52135     getPropertyName : function(name){
52136         var pn = this.grid.propertyNames;
52137         return pn && pn[name] ? pn[name] : name;
52138     },
52139
52140     getCellEditor : function(colIndex, rowIndex){
52141         var p = this.store.getProperty(rowIndex);
52142         var n = p.data['name'], val = p.data['value'];
52143         
52144         if(typeof(this.grid.customEditors[n]) == 'string'){
52145             return this.editors[this.grid.customEditors[n]];
52146         }
52147         if(typeof(this.grid.customEditors[n]) != 'undefined'){
52148             return this.grid.customEditors[n];
52149         }
52150         if(val instanceof Date){
52151             return this.editors['date'];
52152         }else if(typeof val == 'number'){
52153             return this.editors['number'];
52154         }else if(typeof val == 'boolean'){
52155             return this.editors['boolean'];
52156         }else{
52157             return this.editors['string'];
52158         }
52159     }
52160 });
52161
52162 /**
52163  * @class Roo.grid.PropertyGrid
52164  * @extends Roo.grid.EditorGrid
52165  * This class represents the  interface of a component based property grid control.
52166  * <br><br>Usage:<pre><code>
52167  var grid = new Roo.grid.PropertyGrid("my-container-id", {
52168       
52169  });
52170  // set any options
52171  grid.render();
52172  * </code></pre>
52173   
52174  * @constructor
52175  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52176  * The container MUST have some type of size defined for the grid to fill. The container will be
52177  * automatically set to position relative if it isn't already.
52178  * @param {Object} config A config object that sets properties on this grid.
52179  */
52180 Roo.grid.PropertyGrid = function(container, config){
52181     config = config || {};
52182     var store = new Roo.grid.PropertyStore(this);
52183     this.store = store;
52184     var cm = new Roo.grid.PropertyColumnModel(this, store);
52185     store.store.sort('name', 'ASC');
52186     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
52187         ds: store.store,
52188         cm: cm,
52189         enableColLock:false,
52190         enableColumnMove:false,
52191         stripeRows:false,
52192         trackMouseOver: false,
52193         clicksToEdit:1
52194     }, config));
52195     this.getGridEl().addClass('x-props-grid');
52196     this.lastEditRow = null;
52197     this.on('columnresize', this.onColumnResize, this);
52198     this.addEvents({
52199          /**
52200              * @event beforepropertychange
52201              * Fires before a property changes (return false to stop?)
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         "beforepropertychange": true,
52208         /**
52209              * @event propertychange
52210              * Fires after a property changes
52211              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52212              * @param {String} id Record Id
52213              * @param {String} newval New Value
52214          * @param {String} oldval Old Value
52215              */
52216         "propertychange": true
52217     });
52218     this.customEditors = this.customEditors || {};
52219 };
52220 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52221     
52222      /**
52223      * @cfg {Object} customEditors map of colnames=> custom editors.
52224      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52225      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52226      * false disables editing of the field.
52227          */
52228     
52229       /**
52230      * @cfg {Object} propertyNames map of property Names to their displayed value
52231          */
52232     
52233     render : function(){
52234         Roo.grid.PropertyGrid.superclass.render.call(this);
52235         this.autoSize.defer(100, this);
52236     },
52237
52238     autoSize : function(){
52239         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52240         if(this.view){
52241             this.view.fitColumns();
52242         }
52243     },
52244
52245     onColumnResize : function(){
52246         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52247         this.autoSize();
52248     },
52249     /**
52250      * Sets the data for the Grid
52251      * accepts a Key => Value object of all the elements avaiable.
52252      * @param {Object} data  to appear in grid.
52253      */
52254     setSource : function(source){
52255         this.store.setSource(source);
52256         //this.autoSize();
52257     },
52258     /**
52259      * Gets all the data from the grid.
52260      * @return {Object} data  data stored in grid
52261      */
52262     getSource : function(){
52263         return this.store.getSource();
52264     }
52265 });/*
52266  * Based on:
52267  * Ext JS Library 1.1.1
52268  * Copyright(c) 2006-2007, Ext JS, LLC.
52269  *
52270  * Originally Released Under LGPL - original licence link has changed is not relivant.
52271  *
52272  * Fork - LGPL
52273  * <script type="text/javascript">
52274  */
52275  
52276 /**
52277  * @class Roo.LoadMask
52278  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52279  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52280  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52281  * element's UpdateManager load indicator and will be destroyed after the initial load.
52282  * @constructor
52283  * Create a new LoadMask
52284  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52285  * @param {Object} config The config object
52286  */
52287 Roo.LoadMask = function(el, config){
52288     this.el = Roo.get(el);
52289     Roo.apply(this, config);
52290     if(this.store){
52291         this.store.on('beforeload', this.onBeforeLoad, this);
52292         this.store.on('load', this.onLoad, this);
52293         this.store.on('loadexception', this.onLoadException, this);
52294         this.removeMask = false;
52295     }else{
52296         var um = this.el.getUpdateManager();
52297         um.showLoadIndicator = false; // disable the default indicator
52298         um.on('beforeupdate', this.onBeforeLoad, this);
52299         um.on('update', this.onLoad, this);
52300         um.on('failure', this.onLoad, this);
52301         this.removeMask = true;
52302     }
52303 };
52304
52305 Roo.LoadMask.prototype = {
52306     /**
52307      * @cfg {Boolean} removeMask
52308      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52309      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52310      */
52311     /**
52312      * @cfg {String} msg
52313      * The text to display in a centered loading message box (defaults to 'Loading...')
52314      */
52315     msg : 'Loading...',
52316     /**
52317      * @cfg {String} msgCls
52318      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52319      */
52320     msgCls : 'x-mask-loading',
52321
52322     /**
52323      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52324      * @type Boolean
52325      */
52326     disabled: false,
52327
52328     /**
52329      * Disables the mask to prevent it from being displayed
52330      */
52331     disable : function(){
52332        this.disabled = true;
52333     },
52334
52335     /**
52336      * Enables the mask so that it can be displayed
52337      */
52338     enable : function(){
52339         this.disabled = false;
52340     },
52341     
52342     onLoadException : function()
52343     {
52344         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52345             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52346         }
52347         this.el.unmask(this.removeMask);
52348     },
52349     // private
52350     onLoad : function()
52351     {
52352         this.el.unmask(this.removeMask);
52353     },
52354
52355     // private
52356     onBeforeLoad : function(){
52357         if(!this.disabled){
52358             this.el.mask(this.msg, this.msgCls);
52359         }
52360     },
52361
52362     // private
52363     destroy : function(){
52364         if(this.store){
52365             this.store.un('beforeload', this.onBeforeLoad, this);
52366             this.store.un('load', this.onLoad, this);
52367             this.store.un('loadexception', this.onLoadException, this);
52368         }else{
52369             var um = this.el.getUpdateManager();
52370             um.un('beforeupdate', this.onBeforeLoad, this);
52371             um.un('update', this.onLoad, this);
52372             um.un('failure', this.onLoad, this);
52373         }
52374     }
52375 };/*
52376  * Based on:
52377  * Ext JS Library 1.1.1
52378  * Copyright(c) 2006-2007, Ext JS, LLC.
52379  *
52380  * Originally Released Under LGPL - original licence link has changed is not relivant.
52381  *
52382  * Fork - LGPL
52383  * <script type="text/javascript">
52384  */
52385
52386
52387 /**
52388  * @class Roo.XTemplate
52389  * @extends Roo.Template
52390  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
52391 <pre><code>
52392 var t = new Roo.XTemplate(
52393         '&lt;select name="{name}"&gt;',
52394                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
52395         '&lt;/select&gt;'
52396 );
52397  
52398 // then append, applying the master template values
52399  </code></pre>
52400  *
52401  * Supported features:
52402  *
52403  *  Tags:
52404
52405 <pre><code>
52406       {a_variable} - output encoded.
52407       {a_variable.format:("Y-m-d")} - call a method on the variable
52408       {a_variable:raw} - unencoded output
52409       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
52410       {a_variable:this.method_on_template(...)} - call a method on the template object.
52411  
52412 </code></pre>
52413  *  The tpl tag:
52414 <pre><code>
52415         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
52416         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
52417         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
52418         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
52419   
52420         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
52421         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
52422 </code></pre>
52423  *      
52424  */
52425 Roo.XTemplate = function()
52426 {
52427     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52428     if (this.html) {
52429         this.compile();
52430     }
52431 };
52432
52433
52434 Roo.extend(Roo.XTemplate, Roo.Template, {
52435
52436     /**
52437      * The various sub templates
52438      */
52439     tpls : false,
52440     /**
52441      *
52442      * basic tag replacing syntax
52443      * WORD:WORD()
52444      *
52445      * // you can fake an object call by doing this
52446      *  x.t:(test,tesT) 
52447      * 
52448      */
52449     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52450
52451     /**
52452      * compile the template
52453      *
52454      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
52455      *
52456      */
52457     compile: function()
52458     {
52459         var s = this.html;
52460      
52461         s = ['<tpl>', s, '</tpl>'].join('');
52462     
52463         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
52464             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
52465             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
52466             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
52467             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
52468             m,
52469             id     = 0,
52470             tpls   = [];
52471     
52472         while(true == !!(m = s.match(re))){
52473             var forMatch   = m[0].match(nameRe),
52474                 ifMatch   = m[0].match(ifRe),
52475                 execMatch   = m[0].match(execRe),
52476                 namedMatch   = m[0].match(namedRe),
52477                 
52478                 exp  = null, 
52479                 fn   = null,
52480                 exec = null,
52481                 name = forMatch && forMatch[1] ? forMatch[1] : '';
52482                 
52483             if (ifMatch) {
52484                 // if - puts fn into test..
52485                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
52486                 if(exp){
52487                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52488                 }
52489             }
52490             
52491             if (execMatch) {
52492                 // exec - calls a function... returns empty if true is  returned.
52493                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
52494                 if(exp){
52495                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52496                 }
52497             }
52498             
52499             
52500             if (name) {
52501                 // for = 
52502                 switch(name){
52503                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52504                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52505                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52506                 }
52507             }
52508             var uid = namedMatch ? namedMatch[1] : id;
52509             
52510             
52511             tpls.push({
52512                 id:     namedMatch ? namedMatch[1] : id,
52513                 target: name,
52514                 exec:   exec,
52515                 test:   fn,
52516                 body:   m[1] || ''
52517             });
52518             if (namedMatch) {
52519                 s = s.replace(m[0], '');
52520             } else { 
52521                 s = s.replace(m[0], '{xtpl'+ id + '}');
52522             }
52523             ++id;
52524         }
52525         this.tpls = [];
52526         for(var i = tpls.length-1; i >= 0; --i){
52527             this.compileTpl(tpls[i]);
52528             this.tpls[tpls[i].id] = tpls[i];
52529         }
52530         this.master = tpls[tpls.length-1];
52531         return this;
52532     },
52533     /**
52534      * same as applyTemplate, except it's done to one of the subTemplates
52535      * when using named templates, you can do:
52536      *
52537      * var str = pl.applySubTemplate('your-name', values);
52538      *
52539      * 
52540      * @param {Number} id of the template
52541      * @param {Object} values to apply to template
52542      * @param {Object} parent (normaly the instance of this object)
52543      */
52544     applySubTemplate : function(id, values, parent)
52545     {
52546         
52547         
52548         var t = this.tpls[id];
52549         
52550         
52551         try { 
52552             if(t.test && !t.test.call(this, values, parent)){
52553                 return '';
52554             }
52555         } catch(e) {
52556             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
52557             Roo.log(e.toString());
52558             Roo.log(t.test);
52559             return ''
52560         }
52561         try { 
52562             
52563             if(t.exec && t.exec.call(this, values, parent)){
52564                 return '';
52565             }
52566         } catch(e) {
52567             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
52568             Roo.log(e.toString());
52569             Roo.log(t.exec);
52570             return ''
52571         }
52572         try {
52573             var vs = t.target ? t.target.call(this, values, parent) : values;
52574             parent = t.target ? values : parent;
52575             if(t.target && vs instanceof Array){
52576                 var buf = [];
52577                 for(var i = 0, len = vs.length; i < len; i++){
52578                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
52579                 }
52580                 return buf.join('');
52581             }
52582             return t.compiled.call(this, vs, parent);
52583         } catch (e) {
52584             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
52585             Roo.log(e.toString());
52586             Roo.log(t.compiled);
52587             return '';
52588         }
52589     },
52590
52591     compileTpl : function(tpl)
52592     {
52593         var fm = Roo.util.Format;
52594         var useF = this.disableFormats !== true;
52595         var sep = Roo.isGecko ? "+" : ",";
52596         var undef = function(str) {
52597             Roo.log("Property not found :"  + str);
52598             return '';
52599         };
52600         
52601         var fn = function(m, name, format, args)
52602         {
52603             //Roo.log(arguments);
52604             args = args ? args.replace(/\\'/g,"'") : args;
52605             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
52606             if (typeof(format) == 'undefined') {
52607                 format= 'htmlEncode';
52608             }
52609             if (format == 'raw' ) {
52610                 format = false;
52611             }
52612             
52613             if(name.substr(0, 4) == 'xtpl'){
52614                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52615             }
52616             
52617             // build an array of options to determine if value is undefined..
52618             
52619             // basically get 'xxxx.yyyy' then do
52620             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
52621             //    (function () { Roo.log("Property not found"); return ''; })() :
52622             //    ......
52623             
52624             var udef_ar = [];
52625             var lookfor = '';
52626             Roo.each(name.split('.'), function(st) {
52627                 lookfor += (lookfor.length ? '.': '') + st;
52628                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
52629             });
52630             
52631             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
52632             
52633             
52634             if(format && useF){
52635                 
52636                 args = args ? ',' + args : "";
52637                  
52638                 if(format.substr(0, 5) != "this."){
52639                     format = "fm." + format + '(';
52640                 }else{
52641                     format = 'this.call("'+ format.substr(5) + '", ';
52642                     args = ", values";
52643                 }
52644                 
52645                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
52646             }
52647              
52648             if (args.length) {
52649                 // called with xxyx.yuu:(test,test)
52650                 // change to ()
52651                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
52652             }
52653             // raw.. - :raw modifier..
52654             return "'"+ sep + udef_st  + name + ")"+sep+"'";
52655             
52656         };
52657         var body;
52658         // branched to use + in gecko and [].join() in others
52659         if(Roo.isGecko){
52660             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
52661                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52662                     "';};};";
52663         }else{
52664             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
52665             body.push(tpl.body.replace(/(\r\n|\n)/g,
52666                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52667             body.push("'].join('');};};");
52668             body = body.join('');
52669         }
52670         
52671         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
52672        
52673         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
52674         eval(body);
52675         
52676         return this;
52677     },
52678
52679     applyTemplate : function(values){
52680         return this.master.compiled.call(this, values, {});
52681         //var s = this.subs;
52682     },
52683
52684     apply : function(){
52685         return this.applyTemplate.apply(this, arguments);
52686     }
52687
52688  });
52689
52690 Roo.XTemplate.from = function(el){
52691     el = Roo.getDom(el);
52692     return new Roo.XTemplate(el.value || el.innerHTML);
52693 };/*
52694  * Original code for Roojs - LGPL
52695  * <script type="text/javascript">
52696  */
52697  
52698 /**
52699  * @class Roo.XComponent
52700  * A delayed Element creator...
52701  * Or a way to group chunks of interface together.
52702  * 
52703  * Mypart.xyx = new Roo.XComponent({
52704
52705     parent : 'Mypart.xyz', // empty == document.element.!!
52706     order : '001',
52707     name : 'xxxx'
52708     region : 'xxxx'
52709     disabled : function() {} 
52710      
52711     tree : function() { // return an tree of xtype declared components
52712         var MODULE = this;
52713         return 
52714         {
52715             xtype : 'NestedLayoutPanel',
52716             // technicall
52717         }
52718      ]
52719  *})
52720  *
52721  *
52722  * It can be used to build a big heiracy, with parent etc.
52723  * or you can just use this to render a single compoent to a dom element
52724  * MYPART.render(Roo.Element | String(id) | dom_element )
52725  * 
52726  * @extends Roo.util.Observable
52727  * @constructor
52728  * @param cfg {Object} configuration of component
52729  * 
52730  */
52731 Roo.XComponent = function(cfg) {
52732     Roo.apply(this, cfg);
52733     this.addEvents({ 
52734         /**
52735              * @event built
52736              * Fires when this the componnt is built
52737              * @param {Roo.XComponent} c the component
52738              */
52739         'built' : true
52740         
52741     });
52742     this.region = this.region || 'center'; // default..
52743     Roo.XComponent.register(this);
52744     this.modules = false;
52745     this.el = false; // where the layout goes..
52746     
52747     
52748 }
52749 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52750     /**
52751      * @property el
52752      * The created element (with Roo.factory())
52753      * @type {Roo.Layout}
52754      */
52755     el  : false,
52756     
52757     /**
52758      * @property el
52759      * for BC  - use el in new code
52760      * @type {Roo.Layout}
52761      */
52762     panel : false,
52763     
52764     /**
52765      * @property layout
52766      * for BC  - use el in new code
52767      * @type {Roo.Layout}
52768      */
52769     layout : false,
52770     
52771      /**
52772      * @cfg {Function|boolean} disabled
52773      * If this module is disabled by some rule, return true from the funtion
52774      */
52775     disabled : false,
52776     
52777     /**
52778      * @cfg {String} parent 
52779      * Name of parent element which it get xtype added to..
52780      */
52781     parent: false,
52782     
52783     /**
52784      * @cfg {String} order
52785      * Used to set the order in which elements are created (usefull for multiple tabs)
52786      */
52787     
52788     order : false,
52789     /**
52790      * @cfg {String} name
52791      * String to display while loading.
52792      */
52793     name : false,
52794     /**
52795      * @cfg {String} region
52796      * Region to render component to (defaults to center)
52797      */
52798     region : 'center',
52799     
52800     /**
52801      * @cfg {Array} items
52802      * A single item array - the first element is the root of the tree..
52803      * It's done this way to stay compatible with the Xtype system...
52804      */
52805     items : false,
52806     
52807     /**
52808      * @property _tree
52809      * The method that retuns the tree of parts that make up this compoennt 
52810      * @type {function}
52811      */
52812     _tree  : false,
52813     
52814      /**
52815      * render
52816      * render element to dom or tree
52817      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52818      */
52819     
52820     render : function(el)
52821     {
52822         
52823         el = el || false;
52824         var hp = this.parent ? 1 : 0;
52825         
52826         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52827             // if parent is a '#.....' string, then let's use that..
52828             var ename = this.parent.substr(1)
52829             this.parent = false;
52830             el = Roo.get(ename);
52831             if (!el) {
52832                 Roo.log("Warning - element can not be found :#" + ename );
52833                 return;
52834             }
52835         }
52836         
52837         
52838         if (!this.parent) {
52839             
52840             el = el ? Roo.get(el) : false;      
52841             
52842             // it's a top level one..
52843             this.parent =  {
52844                 el : new Roo.BorderLayout(el || document.body, {
52845                 
52846                      center: {
52847                          titlebar: false,
52848                          autoScroll:false,
52849                          closeOnTab: true,
52850                          tabPosition: 'top',
52851                           //resizeTabs: true,
52852                          alwaysShowTabs: el && hp? false :  true,
52853                          hideTabs: el || !hp ? true :  false,
52854                          minTabWidth: 140
52855                      }
52856                  })
52857             }
52858         }
52859         
52860                 
52861                 // The 'tree' method is  '_tree now' 
52862             
52863         var tree = this._tree ? this._tree() : this.tree();
52864         tree.region = tree.region || this.region;
52865         this.el = this.parent.el.addxtype(tree);
52866         this.fireEvent('built', this);
52867         
52868         this.panel = this.el;
52869         this.layout = this.panel.layout;
52870                 this.parentLayout = this.parent.layout  || false;  
52871          
52872     }
52873     
52874 });
52875
52876 Roo.apply(Roo.XComponent, {
52877     
52878     /**
52879      * @property  buildCompleted
52880      * True when the builder has completed building the interface.
52881      * @type Boolean
52882      */
52883     buildCompleted : false,
52884      
52885     /**
52886      * @property  topModule
52887      * the upper most module - uses document.element as it's constructor.
52888      * @type Object
52889      */
52890      
52891     topModule  : false,
52892       
52893     /**
52894      * @property  modules
52895      * array of modules to be created by registration system.
52896      * @type {Array} of Roo.XComponent
52897      */
52898     
52899     modules : [],
52900     /**
52901      * @property  elmodules
52902      * array of modules to be created by which use #ID 
52903      * @type {Array} of Roo.XComponent
52904      */
52905      
52906     elmodules : [],
52907
52908     
52909     /**
52910      * Register components to be built later.
52911      *
52912      * This solves the following issues
52913      * - Building is not done on page load, but after an authentication process has occured.
52914      * - Interface elements are registered on page load
52915      * - Parent Interface elements may not be loaded before child, so this handles that..
52916      * 
52917      *
52918      * example:
52919      * 
52920      * MyApp.register({
52921           order : '000001',
52922           module : 'Pman.Tab.projectMgr',
52923           region : 'center',
52924           parent : 'Pman.layout',
52925           disabled : false,  // or use a function..
52926         })
52927      
52928      * * @param {Object} details about module
52929      */
52930     register : function(obj) {
52931                 
52932                 Roo.XComponent.event.fireEvent('register', obj);
52933                 switch(typeof(obj.disabled) ) {
52934                         
52935                         case 'undefined':
52936                                 break;
52937                         
52938                         case 'function':
52939                                 if ( obj.disabled() ) {
52940                                         return;
52941                                 }
52942                                 break;
52943                         default:
52944                                 if (obj.disabled) {
52945                                         return;
52946                                 }
52947                                 break;
52948                 }
52949                 
52950         this.modules.push(obj);
52951          
52952     },
52953     /**
52954      * convert a string to an object..
52955      * eg. 'AAA.BBB' -> finds AAA.BBB
52956
52957      */
52958     
52959     toObject : function(str)
52960     {
52961         if (!str || typeof(str) == 'object') {
52962             return str;
52963         }
52964         if (str.substring(0,1) == '#') {
52965             return str;
52966         }
52967
52968         var ar = str.split('.');
52969         var rt, o;
52970         rt = ar.shift();
52971             /** eval:var:o */
52972         try {
52973             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52974         } catch (e) {
52975             throw "Module not found : " + str;
52976         }
52977         
52978         if (o === false) {
52979             throw "Module not found : " + str;
52980         }
52981         Roo.each(ar, function(e) {
52982             if (typeof(o[e]) == 'undefined') {
52983                 throw "Module not found : " + str;
52984             }
52985             o = o[e];
52986         });
52987         
52988         return o;
52989         
52990     },
52991     
52992     
52993     /**
52994      * move modules into their correct place in the tree..
52995      * 
52996      */
52997     preBuild : function ()
52998     {
52999         var _t = this;
53000         Roo.each(this.modules , function (obj)
53001         {
53002             var opar = obj.parent;
53003             try { 
53004                 obj.parent = this.toObject(opar);
53005             } catch(e) {
53006                 Roo.log("parent:toObject failed: " + e.toString());
53007                 return;
53008             }
53009             
53010             if (!obj.parent) {
53011                                 Roo.debug && Roo.log("GOT top level module");
53012                                 Roo.debug && Roo.log(obj);
53013                                 obj.modules = new Roo.util.MixedCollection(false, 
53014                     function(o) { return o.order + '' }
53015                 );
53016                 this.topModule = obj;
53017                 return;
53018             }
53019                         // parent is a string (usually a dom element name..)
53020             if (typeof(obj.parent) == 'string') {
53021                 this.elmodules.push(obj);
53022                 return;
53023             }
53024             if (obj.parent.constructor != Roo.XComponent) {
53025                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
53026             }
53027             if (!obj.parent.modules) {
53028                 obj.parent.modules = new Roo.util.MixedCollection(false, 
53029                     function(o) { return o.order + '' }
53030                 );
53031             }
53032             
53033             obj.parent.modules.add(obj);
53034         }, this);
53035     },
53036     
53037      /**
53038      * make a list of modules to build.
53039      * @return {Array} list of modules. 
53040      */ 
53041     
53042     buildOrder : function()
53043     {
53044         var _this = this;
53045         var cmp = function(a,b) {   
53046             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
53047         };
53048         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
53049             throw "No top level modules to build";
53050         }
53051         
53052         // make a flat list in order of modules to build.
53053         var mods = this.topModule ? [ this.topModule ] : [];
53054                 
53055                 // elmodules (is a list of DOM based modules )
53056         Roo.each(this.elmodules,function(e) { mods.push(e) });
53057
53058         
53059         // add modules to their parents..
53060         var addMod = function(m) {
53061                         Roo.debug && Roo.log("build Order: add: " + m.name);
53062             
53063             mods.push(m);
53064             if (m.modules) {
53065                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
53066                 m.modules.keySort('ASC',  cmp );
53067                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
53068
53069                 m.modules.each(addMod);
53070             } else {
53071                                 Roo.debug && Roo.log("build Order: no child modules");
53072                         }
53073             // not sure if this is used any more..
53074             if (m.finalize) {
53075                 m.finalize.name = m.name + " (clean up) ";
53076                 mods.push(m.finalize);
53077             }
53078             
53079         }
53080         if (this.topModule) { 
53081             this.topModule.modules.keySort('ASC',  cmp );
53082             this.topModule.modules.each(addMod);
53083         }
53084         return mods;
53085     },
53086     
53087      /**
53088      * Build the registered modules.
53089      * @param {Object} parent element.
53090      * @param {Function} optional method to call after module has been added.
53091      * 
53092      */ 
53093    
53094     build : function() 
53095     {
53096         
53097         this.preBuild();
53098         var mods = this.buildOrder();
53099       
53100         //this.allmods = mods;
53101         //Roo.debug && Roo.log(mods);
53102         //return;
53103         if (!mods.length) { // should not happen
53104             throw "NO modules!!!";
53105         }
53106         
53107         
53108         var msg = "Building Interface...";
53109         // flash it up as modal - so we store the mask!?
53110         Roo.MessageBox.show({ title: 'loading' });
53111         Roo.MessageBox.show({
53112            title: "Please wait...",
53113            msg: msg,
53114            width:450,
53115            progress:true,
53116            closable:false,
53117            modal: false
53118           
53119         });
53120         var total = mods.length;
53121         
53122         var _this = this;
53123         var progressRun = function() {
53124             if (!mods.length) {
53125                 Roo.debug && Roo.log('hide?');
53126                 Roo.MessageBox.hide();
53127                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
53128                 
53129                 // THE END...
53130                 return false;   
53131             }
53132             
53133             var m = mods.shift();
53134             
53135             
53136             Roo.debug && Roo.log(m);
53137             // not sure if this is supported any more.. - modules that are are just function
53138             if (typeof(m) == 'function') { 
53139                 m.call(this);
53140                 return progressRun.defer(10, _this);
53141             } 
53142             
53143             
53144             msg = "Building Interface " + (total  - mods.length) + 
53145                     " of " + total + 
53146                     (m.name ? (' - ' + m.name) : '');
53147                         Roo.debug && Roo.log(msg);
53148             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
53149             
53150          
53151             // is the module disabled?
53152             var disabled = (typeof(m.disabled) == 'function') ?
53153                 m.disabled.call(m.module.disabled) : m.disabled;    
53154             
53155             
53156             if (disabled) {
53157                 return progressRun(); // we do not update the display!
53158             }
53159             
53160             // now build 
53161             
53162                         
53163                         
53164             m.render();
53165             // it's 10 on top level, and 1 on others??? why...
53166             return progressRun.defer(10, _this);
53167              
53168         }
53169         progressRun.defer(1, _this);
53170      
53171         
53172         
53173     },
53174         
53175         
53176         /**
53177          * Event Object.
53178          *
53179          *
53180          */
53181         event: false, 
53182     /**
53183          * wrapper for event.on - aliased later..  
53184          * Typically use to register a event handler for register:
53185          *
53186          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
53187          *
53188          */
53189     on : false
53190    
53191     
53192     
53193 });
53194
53195 Roo.XComponent.event = new Roo.util.Observable({
53196                 events : { 
53197                         /**
53198                          * @event register
53199                          * Fires when an Component is registered,
53200                          * set the disable property on the Component to stop registration.
53201                          * @param {Roo.XComponent} c the component being registerd.
53202                          * 
53203                          */
53204                         'register' : true,
53205                         /**
53206                          * @event buildcomplete
53207                          * Fires on the top level element when all elements have been built
53208                          * @param {Roo.XComponent} the top level component.
53209                          */
53210                         'buildcomplete' : true
53211                         
53212                 }
53213 });
53214
53215 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
53216  //<script type="text/javascript">
53217
53218
53219 /**
53220  * @class Roo.Login
53221  * @extends Roo.LayoutDialog
53222  * A generic Login Dialog..... - only one needed in theory!?!?
53223  *
53224  * Fires XComponent builder on success...
53225  * 
53226  * Sends 
53227  *    username,password, lang = for login actions.
53228  *    check = 1 for periodic checking that sesion is valid.
53229  *    passwordRequest = email request password
53230  *    logout = 1 = to logout
53231  * 
53232  * Affects: (this id="????" elements)
53233  *   loading  (removed) (used to indicate application is loading)
53234  *   loading-mask (hides) (used to hide application when it's building loading)
53235  *   
53236  * 
53237  * Usage: 
53238  *    
53239  * 
53240  * Myapp.login = Roo.Login({
53241      url: xxxx,
53242    
53243      realm : 'Myapp', 
53244      
53245      
53246      method : 'POST',
53247      
53248      
53249      * 
53250  })
53251  * 
53252  * 
53253  * 
53254  **/
53255  
53256 Roo.Login = function(cfg)
53257 {
53258     this.addEvents({
53259         'refreshed' : true
53260     });
53261     
53262     Roo.apply(this,cfg);
53263     
53264     Roo.onReady(function() {
53265         this.onLoad();
53266     }, this);
53267     // call parent..
53268     
53269    
53270     Roo.Login.superclass.constructor.call(this, this);
53271     //this.addxtype(this.items[0]);
53272     
53273     
53274 }
53275
53276
53277 Roo.extend(Roo.Login, Roo.LayoutDialog, {
53278     
53279     /**
53280      * @cfg {String} method
53281      * Method used to query for login details.
53282      */
53283     
53284     method : 'POST',
53285     /**
53286      * @cfg {String} url
53287      * URL to query login data. - eg. baseURL + '/Login.php'
53288      */
53289     url : '',
53290     
53291     /**
53292      * @property user
53293      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
53294      * @type {Object} 
53295      */
53296     user : false,
53297     /**
53298      * @property checkFails
53299      * Number of times we have attempted to get authentication check, and failed.
53300      * @type {Number} 
53301      */
53302     checkFails : 0,
53303       /**
53304      * @property intervalID
53305      * The window interval that does the constant login checking.
53306      * @type {Number} 
53307      */
53308     intervalID : 0,
53309     
53310     
53311     onLoad : function() // called on page load...
53312     {
53313         // load 
53314          
53315         if (Roo.get('loading')) { // clear any loading indicator..
53316             Roo.get('loading').remove();
53317         }
53318         
53319         //this.switchLang('en'); // set the language to english..
53320        
53321         this.check({
53322             success:  function(response, opts)  {  // check successfull...
53323             
53324                 var res = this.processResponse(response);
53325                 this.checkFails =0;
53326                 if (!res.success) { // error!
53327                     this.checkFails = 5;
53328                     //console.log('call failure');
53329                     return this.failure(response,opts);
53330                 }
53331                 
53332                 if (!res.data.id) { // id=0 == login failure.
53333                     return this.show();
53334                 }
53335                 
53336                               
53337                         //console.log(success);
53338                 this.fillAuth(res.data);   
53339                 this.checkFails =0;
53340                 Roo.XComponent.build();
53341             },
53342             failure : this.show
53343         });
53344         
53345     }, 
53346     
53347     
53348     check: function(cfg) // called every so often to refresh cookie etc..
53349     {
53350         if (cfg.again) { // could be undefined..
53351             this.checkFails++;
53352         } else {
53353             this.checkFails = 0;
53354         }
53355         var _this = this;
53356         if (this.sending) {
53357             if ( this.checkFails > 4) {
53358                 Roo.MessageBox.alert("Error",  
53359                     "Error getting authentication status. - try reloading, or wait a while", function() {
53360                         _this.sending = false;
53361                     }); 
53362                 return;
53363             }
53364             cfg.again = true;
53365             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
53366             return;
53367         }
53368         this.sending = true;
53369         
53370         Roo.Ajax.request({  
53371             url: this.url,
53372             params: {
53373                 getAuthUser: true
53374             },  
53375             method: this.method,
53376             success:  cfg.success || this.success,
53377             failure : cfg.failure || this.failure,
53378             scope : this,
53379             callCfg : cfg
53380               
53381         });  
53382     }, 
53383     
53384     
53385     logout: function()
53386     {
53387         window.onbeforeunload = function() { }; // false does not work for IE..
53388         this.user = false;
53389         var _this = this;
53390         
53391         Roo.Ajax.request({  
53392             url: this.url,
53393             params: {
53394                 logout: 1
53395             },  
53396             method: 'GET',
53397             failure : function() {
53398                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
53399                     document.location = document.location.toString() + '?ts=' + Math.random();
53400                 });
53401                 
53402             },
53403             success : function() {
53404                 _this.user = false;
53405                 this.checkFails =0;
53406                 // fixme..
53407                 document.location = document.location.toString() + '?ts=' + Math.random();
53408             }
53409               
53410               
53411         }); 
53412     },
53413     
53414     processResponse : function (response)
53415     {
53416         var res = '';
53417         try {
53418             res = Roo.decode(response.responseText);
53419             // oops...
53420             if (typeof(res) != 'object') {
53421                 res = { success : false, errorMsg : res, errors : true };
53422             }
53423             if (typeof(res.success) == 'undefined') {
53424                 res.success = false;
53425             }
53426             
53427         } catch(e) {
53428             res = { success : false,  errorMsg : response.responseText, errors : true };
53429         }
53430         return res;
53431     },
53432     
53433     success : function(response, opts)  // check successfull...
53434     {  
53435         this.sending = false;
53436         var res = this.processResponse(response);
53437         if (!res.success) {
53438             return this.failure(response, opts);
53439         }
53440         if (!res.data || !res.data.id) {
53441             return this.failure(response,opts);
53442         }
53443         //console.log(res);
53444         this.fillAuth(res.data);
53445         
53446         this.checkFails =0;
53447         
53448     },
53449     
53450     
53451     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53452     {
53453         this.authUser = -1;
53454         this.sending = false;
53455         var res = this.processResponse(response);
53456         //console.log(res);
53457         if ( this.checkFails > 2) {
53458         
53459             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53460                 "Error getting authentication status. - try reloading"); 
53461             return;
53462         }
53463         opts.callCfg.again = true;
53464         this.check.defer(1000, this, [ opts.callCfg ]);
53465         return;  
53466     },
53467     
53468     
53469     
53470     fillAuth: function(au) {
53471         this.startAuthCheck();
53472         this.authUserId = au.id;
53473         this.authUser = au;
53474         this.lastChecked = new Date();
53475         this.fireEvent('refreshed', au);
53476         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53477         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53478         au.lang = au.lang || 'en';
53479         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53480         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53481         this.switchLang(au.lang );
53482         
53483      
53484         // open system... - -on setyp..
53485         if (this.authUserId  < 0) {
53486             Roo.MessageBox.alert("Warning", 
53487                 "This is an open system - please set up a admin user with a password.");  
53488         }
53489          
53490         //Pman.onload(); // which should do nothing if it's a re-auth result...
53491         
53492              
53493     },
53494     
53495     startAuthCheck : function() // starter for timeout checking..
53496     {
53497         if (this.intervalID) { // timer already in place...
53498             return false;
53499         }
53500         var _this = this;
53501         this.intervalID =  window.setInterval(function() {
53502               _this.check(false);
53503             }, 120000); // every 120 secs = 2mins..
53504         
53505         
53506     },
53507          
53508     
53509     switchLang : function (lang) 
53510     {
53511         _T = typeof(_T) == 'undefined' ? false : _T;
53512           if (!_T || !lang.length) {
53513             return;
53514         }
53515         
53516         if (!_T && lang != 'en') {
53517             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53518             return;
53519         }
53520         
53521         if (typeof(_T.en) == 'undefined') {
53522             _T.en = {};
53523             Roo.apply(_T.en, _T);
53524         }
53525         
53526         if (typeof(_T[lang]) == 'undefined') {
53527             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53528             return;
53529         }
53530         
53531         
53532         Roo.apply(_T, _T[lang]);
53533         // just need to set the text values for everything...
53534         var _this = this;
53535         /* this will not work ...
53536         if (this.form) { 
53537             
53538                
53539             function formLabel(name, val) {
53540                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53541             }
53542             
53543             formLabel('password', "Password"+':');
53544             formLabel('username', "Email Address"+':');
53545             formLabel('lang', "Language"+':');
53546             this.dialog.setTitle("Login");
53547             this.dialog.buttons[0].setText("Forgot Password");
53548             this.dialog.buttons[1].setText("Login");
53549         }
53550         */
53551         
53552         
53553     },
53554     
53555     
53556     title: "Login",
53557     modal: true,
53558     width:  350,
53559     //height: 230,
53560     height: 180,
53561     shadow: true,
53562     minWidth:200,
53563     minHeight:180,
53564     //proxyDrag: true,
53565     closable: false,
53566     draggable: false,
53567     collapsible: false,
53568     resizable: false,
53569     center: {  // needed??
53570         autoScroll:false,
53571         titlebar: false,
53572        // tabPosition: 'top',
53573         hideTabs: true,
53574         closeOnTab: true,
53575         alwaysShowTabs: false
53576     } ,
53577     listeners : {
53578         
53579         show  : function(dlg)
53580         {
53581             //console.log(this);
53582             this.form = this.layout.getRegion('center').activePanel.form;
53583             this.form.dialog = dlg;
53584             this.buttons[0].form = this.form;
53585             this.buttons[0].dialog = dlg;
53586             this.buttons[1].form = this.form;
53587             this.buttons[1].dialog = dlg;
53588            
53589            //this.resizeToLogo.defer(1000,this);
53590             // this is all related to resizing for logos..
53591             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53592            //// if (!sz) {
53593              //   this.resizeToLogo.defer(1000,this);
53594              //   return;
53595            // }
53596             //var w = Ext.lib.Dom.getViewWidth() - 100;
53597             //var h = Ext.lib.Dom.getViewHeight() - 100;
53598             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53599             //this.center();
53600             if (this.disabled) {
53601                 this.hide();
53602                 return;
53603             }
53604             
53605             if (this.user.id < 0) { // used for inital setup situations.
53606                 return;
53607             }
53608             
53609             if (this.intervalID) {
53610                 // remove the timer
53611                 window.clearInterval(this.intervalID);
53612                 this.intervalID = false;
53613             }
53614             
53615             
53616             if (Roo.get('loading')) {
53617                 Roo.get('loading').remove();
53618             }
53619             if (Roo.get('loading-mask')) {
53620                 Roo.get('loading-mask').hide();
53621             }
53622             
53623             //incomming._node = tnode;
53624             this.form.reset();
53625             //this.dialog.modal = !modal;
53626             //this.dialog.show();
53627             this.el.unmask(); 
53628             
53629             
53630             this.form.setValues({
53631                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53632                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53633             });
53634             
53635             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53636             if (this.form.findField('username').getValue().length > 0 ){
53637                 this.form.findField('password').focus();
53638             } else {
53639                this.form.findField('username').focus();
53640             }
53641     
53642         }
53643     },
53644     items : [
53645          {
53646        
53647             xtype : 'ContentPanel',
53648             xns : Roo,
53649             region: 'center',
53650             fitToFrame : true,
53651             
53652             items : [
53653     
53654                 {
53655                
53656                     xtype : 'Form',
53657                     xns : Roo.form,
53658                     labelWidth: 100,
53659                     style : 'margin: 10px;',
53660                     
53661                     listeners : {
53662                         actionfailed : function(f, act) {
53663                             // form can return { errors: .... }
53664                                 
53665                             //act.result.errors // invalid form element list...
53666                             //act.result.errorMsg// invalid form element list...
53667                             
53668                             this.dialog.el.unmask();
53669                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53670                                         "Login failed - communication error - try again.");
53671                                       
53672                         },
53673                         actioncomplete: function(re, act) {
53674                              
53675                             Roo.state.Manager.set(
53676                                 this.dialog.realm + '.username',  
53677                                     this.findField('username').getValue()
53678                             );
53679                             Roo.state.Manager.set(
53680                                 this.dialog.realm + '.lang',  
53681                                 this.findField('lang').getValue() 
53682                             );
53683                             
53684                             this.dialog.fillAuth(act.result.data);
53685                               
53686                             this.dialog.hide();
53687                             
53688                             if (Roo.get('loading-mask')) {
53689                                 Roo.get('loading-mask').show();
53690                             }
53691                             Roo.XComponent.build();
53692                             
53693                              
53694                             
53695                         }
53696                     },
53697                     items : [
53698                         {
53699                             xtype : 'TextField',
53700                             xns : Roo.form,
53701                             fieldLabel: "Email Address",
53702                             name: 'username',
53703                             width:200,
53704                             autoCreate : {tag: "input", type: "text", size: "20"}
53705                         },
53706                         {
53707                             xtype : 'TextField',
53708                             xns : Roo.form,
53709                             fieldLabel: "Password",
53710                             inputType: 'password',
53711                             name: 'password',
53712                             width:200,
53713                             autoCreate : {tag: "input", type: "text", size: "20"},
53714                             listeners : {
53715                                 specialkey : function(e,ev) {
53716                                     if (ev.keyCode == 13) {
53717                                         this.form.dialog.el.mask("Logging in");
53718                                         this.form.doAction('submit', {
53719                                             url: this.form.dialog.url,
53720                                             method: this.form.dialog.method
53721                                         });
53722                                     }
53723                                 }
53724                             }  
53725                         },
53726                         {
53727                             xtype : 'ComboBox',
53728                             xns : Roo.form,
53729                             fieldLabel: "Language",
53730                             name : 'langdisp',
53731                             store: {
53732                                 xtype : 'SimpleStore',
53733                                 fields: ['lang', 'ldisp'],
53734                                 data : [
53735                                     [ 'en', 'English' ],
53736                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53737                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53738                                 ]
53739                             },
53740                             
53741                             valueField : 'lang',
53742                             hiddenName:  'lang',
53743                             width: 200,
53744                             displayField:'ldisp',
53745                             typeAhead: false,
53746                             editable: false,
53747                             mode: 'local',
53748                             triggerAction: 'all',
53749                             emptyText:'Select a Language...',
53750                             selectOnFocus:true,
53751                             listeners : {
53752                                 select :  function(cb, rec, ix) {
53753                                     this.form.switchLang(rec.data.lang);
53754                                 }
53755                             }
53756                         
53757                         }
53758                     ]
53759                 }
53760                   
53761                 
53762             ]
53763         }
53764     ],
53765     buttons : [
53766         {
53767             xtype : 'Button',
53768             xns : 'Roo',
53769             text : "Forgot Password",
53770             listeners : {
53771                 click : function() {
53772                     //console.log(this);
53773                     var n = this.form.findField('username').getValue();
53774                     if (!n.length) {
53775                         Roo.MessageBox.alert("Error", "Fill in your email address");
53776                         return;
53777                     }
53778                     Roo.Ajax.request({
53779                         url: this.dialog.url,
53780                         params: {
53781                             passwordRequest: n
53782                         },
53783                         method: this.dialog.method,
53784                         success:  function(response, opts)  {  // check successfull...
53785                         
53786                             var res = this.dialog.processResponse(response);
53787                             if (!res.success) { // error!
53788                                Roo.MessageBox.alert("Error" ,
53789                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53790                                return;
53791                             }
53792                             Roo.MessageBox.alert("Notice" ,
53793                                 "Please check you email for the Password Reset message");
53794                         },
53795                         failure : function() {
53796                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53797                         }
53798                         
53799                     });
53800                 }
53801             }
53802         },
53803         {
53804             xtype : 'Button',
53805             xns : 'Roo',
53806             text : "Login",
53807             listeners : {
53808                 
53809                 click : function () {
53810                         
53811                     this.dialog.el.mask("Logging in");
53812                     this.form.doAction('submit', {
53813                             url: this.dialog.url,
53814                             method: this.dialog.method
53815                     });
53816                 }
53817             }
53818         }
53819     ]
53820   
53821   
53822 })
53823  
53824
53825
53826